fit an xtable to a pdf file - r

I am creating a sweave document that uses xtable to create table and puts into a pdf file. It works but table is not fitting the document and some text are missing. Is there a way to text align in xtable/fully fit an xtable to a pdf file?
This is my data:
dput(x)
structure(list(App = structure(c(3L, 2L, 1L), .Label = c("AppServer",
"Db", "Web"), class = "factor"), Group = structure(c(2L, 1L,
1L), .Label = c("Back", "Front"), class = "factor"), Owner = structure(c(1L,
1L, 1L), .Label = "Infrasructure", class = "factor"), Server = structure(1:3, .Label = c("ServerA",
"ServerB", "ServerC"), class = "factor"), NumberCPU = c(64L,
120L, 120L), Description = structure(c(1L, 3L, 2L), .Label = c("Front End server to server web traffic",
"Hold Web templates to generate dynamic content", "Keeps customer data and login information"
), class = "factor"), Cost = structure(1:3, .Label = c("$200,000 ",
"$400,000 ", "$500,000 "), class = "factor")), .Names = c("App",
"Group", "Owner", "Server", "NumberCPU", "Description", "Cost"
), class = "data.frame", row.names = c(NA, -3L))
this is the code to put the table in pdf:
print(xtable(x, caption=paste("Summary of applications"),table.placement="!h",caption.placement="top", align=c('l', 'p{1.5in}', rep('c',6) )))

I recommend checking out the xtable gallery, there are lots of examples that are useful. Basically, If you don't want to adjust your table by shorting the strings, I see two options:
Use a smaller font.
Use landscape mode.
Here, I use a combination of both:
\documentclass{article}
\usepackage{rotating}
\begin{document}
<<Data,echo=FALSE>>=
library(xtable)
x <- structure(list(App = structure(c(3L, 2L, 1L), .Label = c("AppServer",
"Db", "Web"), class = "factor"), Group = structure(c(2L, 1L,
1L), .Label = c("Back", "Front"), class = "factor"), Owner = structure(c(1L,
1L, 1L), .Label = "Infrasructure", class = "factor"), Server = structure(1:3, .Label = c("ServerA",
"ServerB", "ServerC"), class = "factor"), NumberCPU = c(64L,
120L, 120L), Description = structure(c(1L, 3L, 2L), .Label = c("Front End server to server web traffic",
"Hold Web templates to generate dynamic content", "Keeps customer data and login information"
), class = "factor"), Cost = structure(1:3, .Label = c("$200,000 ",
"$400,000 ", "$500,000 "), class = "factor")), .Names = c("App",
"Group", "Owner", "Server", "NumberCPU", "Description", "Cost"
), class = "data.frame", row.names = c(NA, -3L))
#
<<tab,echo=FALSE,results='asis'>>=
print(xtable(x, caption=paste("Summary of applications"),
caption.placement="top",
align=c('l', 'p{1.5in}', rep('c',6))),
size="footnotesize",
floating.environment="sidewaystable")
#
\end{document}
Note that you have to use the LaTex package rotating. This should give you something like this:

An alternative solution would be to use some markdown backend (knitr, markdown or pander packages) and split the table automatically to 80 character (or other user specified width) with pander. E.g.:
> library(pander)
> res <- structure(list(App = structure(c(3L, 2L, 1L), .Label = c("AppServer", "Db", "Web"), class = "factor"), Group = structure(c(2L, 1L, 1L), .Label = c("Back", "Front"), class = "factor"), Owner = structure(c(1L, 1L, 1L), .Label = "Infrasructure", class = "factor"), Server = structure(1:3, .Label = c("ServerA", "ServerB", "ServerC"), class = "factor"), NumberCPU = c(64L, 120L, 120L), Description = structure(c(1L, 3L, 2L), .Label = c("Front End server to server web traffic", "Hold Web templates to generate dynamic content", "Keeps customer data and login information"), class = "factor"), Cost = structure(1:3, .Label = c("$200,000 ", "$400,000 ", "$500,000 "), class = "factor")), .Names = c("App", "Group", "Owner", "Server", "NumberCPU", "Description", "Cost"), class = "data.frame", row.names = c(NA, -3L))
> pander(res)
----------------------------------------------------
App Group Owner Server NumberCPU
--------- ------- ------------- -------- -----------
Web Front Infrasructure ServerA 64
Db Back Infrasructure ServerB 120
AppServer Back Infrasructure ServerC 120
----------------------------------------------------
Table: Table continues below
---------------------------------------
Description Cost
------------------------------ --------
Front End server to server web $200,000
traffic
Keeps customer data and login $400,000
information
Hold Web templates to generate $500,000
dynamic content
---------------------------------------
And the result can be converted to pdf or LaTeX easily then with pandoc or with pander directly from R.

Related

Show empty table before filtering with crosstalk

I would like to filter a reactable with crosstalk. Let's say I have the following Rmd-file:
---
title: "blabla"
output:
flexdashboard::flex_dashboard:
orientation: rows
social: menu
source_code: embed
theme: cerulean
---
```{r}
library(crosstalk)
library(tidyverse)
```
```{r Make dataset}
df1 <- structure(list(owner = structure(c(1L, 2L, 2L, 2L, 2L), .Label = c("John", "Mark"),
class = "factor"), hp = c(250, 120, 250, 100, 110), car = structure(c(2L, 2L, 2L, 1L, 1L), .Label = c("benz", "bmw"), class = "factor"), id = structure(1:5, .Label = c("car1", "car2", "car3", "car4", "car5"), class = "factor")), .Names = c("owner", "hp", "car", "id"), row.names = c(NA, -5L), class = "data.frame")
df2 <- structure(list(car = structure(c(1L, 2L, 1L, 2L), .Label = c("benz",
"bmw"), class = "factor"), owner = structure(c(1L, 1L, 2L, 2L
), .Label = c("John", "Mark"), class = "factor"), freq = c(0L,
1L, 2L, 2L)), .Names = c("car", "owner", "freq"), row.names = c(NA,
-4L), class = "data.frame")
```
#
##
### Filters
```{r}
library(crosstalk)
# Notice the 'group = ' argument - this does the trick!
shared_df1 <- SharedData$new(df1, ~owner, group = "Choose owner")
shared_df2 <- SharedData$new(df2, ~owner, group = "Choose owner")
filter_select("owner", "Car owner:", shared_df1, ~owner)
```
##
### Dataframe 1
```{r}
reactable::reactable(shared_df1)
```
### Dataframe 2
```{r}
reactable::reactable(shared_df2)
```
Now before the user enters a car owner the table shows the rows for all car owners. I would like to achieve that the table is empty, and only if a car owner is entered, the table should show the information for this car owner. Is this possible to achieve? I am looking for a solution without Shiny.
The example above is deduced from: Filter two tables with crosstalk.
I don't know how to do it with crosstalk and tried to search for their docs and maybe this is not supported. BUT, we can do it with some javascripts.
function toggleDF(hasItem, selector) {
if(!hasItem) {
$(selector).hide();
} else {
$(selector).show();
}
}
$(function(){
// hide on start
toggleDF(false, '#dataframe-1 .ReactTable .rt-tbody');
toggleDF(false, '#dataframe-2 .ReactTable .rt-tbody');
$('#owner').on("change", function(){
var hasItem = $(this).find('.selectize-input').hasClass('has-items');
// hide/show based on selection
toggleDF(hasItem, '#dataframe-1 .ReactTable .rt-tbody');
toggleDF(hasItem, '#dataframe-2 .ReactTable .rt-tbody');
});
});
A function toggleDF to show and hide the table body.
Some expressions to trigger the function based on whether you have selections in the owner dropdown.
In the expression, it requires you to put the reactable under certain title so we can find the ID, eg ### Dataframe 2's ID is dataframe-2, lower case of all letters and dash replace spaces (how Rmarkdown translate titles to IDs).
Full code
---
title: "blabla"
output:
flexdashboard::flex_dashboard:
orientation: rows
social: menu
source_code: embed
theme: cerulean
---
```{r}
library(crosstalk)
library(tidyverse)
```
```{r Make dataset}
df1 <- structure(list(owner = structure(c(1L, 2L, 2L, 2L, 2L), .Label = c("John", "Mark"),
class = "factor"), hp = c(250, 120, 250, 100, 110), car = structure(c(2L, 2L, 2L, 1L, 1L), .Label = c("benz", "bmw"), class = "factor"), id = structure(1:5, .Label = c("car1", "car2", "car3", "car4", "car5"), class = "factor")), .Names = c("owner", "hp", "car", "id"), row.names = c(NA, -5L), class = "data.frame")
df2 <- structure(list(car = structure(c(1L, 2L, 1L, 2L), .Label = c("benz",
"bmw"), class = "factor"), owner = structure(c(1L, 1L, 2L, 2L
), .Label = c("John", "Mark"), class = "factor"), freq = c(0L,
1L, 2L, 2L)), .Names = c("car", "owner", "freq"), row.names = c(NA,
-4L), class = "data.frame")
```
#
##
### Filters
```{r}
library(crosstalk)
# Notice the 'group = ' argument - this does the trick!
shared_df1 <- SharedData$new(df1, ~owner, group = "Choose owner")
shared_df2 <- SharedData$new(df2, ~owner, group = "Choose owner")
filter_select("owner", "Car owner:", shared_df1, ~owner)
```
##
### Dataframe 1
```{r}
reactable::reactable(shared_df1)
```
### Dataframe 2
```{r}
reactable::reactable(shared_df2)
```
<script>
function toggleDF(hasItem, selector) {
if(!hasItem) {
$(selector).hide();
} else {
$(selector).show();
}
}
$(function(){
// hide on start
toggleDF(false, '#dataframe-1 .ReactTable .rt-tbody');
toggleDF(false, '#dataframe-2 .ReactTable .rt-tbody');
$('#owner').on("change", function(){
var hasItem = $(this).find('.selectize-input').hasClass('has-items');
// hide/show based on selection
toggleDF(hasItem, '#dataframe-1 .ReactTable .rt-tbody');
toggleDF(hasItem, '#dataframe-2 .ReactTable .rt-tbody');
});
});
</script>

Conditionally adding characters to new column based on separate dataset

Hello all and thank you in advance.
I would like to add a new column to my pre-existing data frame where the values sourced from a second data frame based on certain conditions. The dataset I wish to add the new column to ("data_melt") has many different sample IDs (sample.#) under the variable column. Using a second dataset ("metadata") I want to add the pond names to the "data_melt" new column based on the sample-ids. The sample IDs are the same in both datasets.
My gut tells me there's an obvious solution but my head is pretty fried. Here is a toy example of my data_melt df (since its 25,000 observations):
> dput(toy)
structure(list(gene = c("serA", "mdh", "fdhB", "fdhA"), process = structure(c(1L,
1L, 1L, 1L), .Label = "energy", class = "factor"), category = structure(c(1L,
1L, 1L, 1L), .Label = "metabolism", class = "factor"), ko = structure(1:4, .Label = c("K00058",
"K00093", "K00125", "K00148"), class = "factor"), variable = structure(c(1L,
2L, 3L, 3L), .Label = c("sample.10", "sample.19", "sample.72"
), class = "factor"), value = c(0.00116, 2.77e-05, 1.84e-05,
0.0125)), row.names = c(NA, -4L), class = "data.frame")
And here is a toy example of my metadata df:
> dput(toy)
structure(list(sample = c("sample.10", "sample.19", "sample.72",
"sample.13"), pond = structure(c(2L, 2L, 1L, 1L), .Label = c("lower",
"upper"), class = "factor")), row.names = c(NA, -4L), class = "data.frame")
Thank you again!
We can use match from base R to create a numeric index to replace the values
toy$pond <- with(toy, out$pond[match(variable, out$sample)])
I believe merge will work here.
sss <- structure(list(gene = c("serA", "mdh", "fdhB", "fdhA"), process = structure(c(1L,
1L, 1L, 1L), .Label = "energy", class = "factor"), category = structure(c(1L,
1L, 1L, 1L), .Label = "metabolism", class = "factor"), ko = structure(1:4, .Label = c("K00058",
"K00093", "K00125", "K00148"), class = "factor"), variable = structure(c(1L,
2L, 3L, 3L), .Label = c("sample.10", "sample.19", "sample.72"
), class = "factor"), value = c(0.00116, 2.77e-05, 1.84e-05,
0.0125)), row.names = c(NA, -4L), class = "data.frame")
ss <- structure(list(sample = c("sample.10", "sample.19", "sample.72",
"sample.13"), pond = structure(c(2L, 2L, 1L, 1L), .Label = c("lower",
"upper"), class = "factor")), row.names = c(NA, -4L), class = "data.frame")
ssss <- merge(sss, ss, by.x = "variable", by.y = "sample")
You can use left_join() from the dplyr package after renaming sample to variable in the metadata data frame.
library(tidyverse)
data_melt <- structure(list(gene = c("serA", "mdh", "fdhB", "fdhA"),
process = structure(c(1L, 1L, 1L, 1L),
.Label = "energy",
class = "factor"),
category = structure(c(1L, 1L, 1L, 1L),
.Label = "metabolism",
class = "factor"),
ko = structure(1:4,
.Label = c("K00058", "K00093", "K00125", "K00148"),
class = "factor"),
variable = structure(c(1L, 2L, 3L, 3L),
.Label = c("sample.10", "sample.19", "sample.72"),
class = "factor"),
value = c(0.00116, 2.77e-05, 1.84e-05, 0.0125)),
row.names = c(NA, -4L),
class = "data.frame")
metadata <- structure(list(sample = c("sample.10", "sample.19", "sample.72", "sample.13"),
pond = structure(c(2L, 2L, 1L, 1L),
.Label = c("lower", "upper"),
class = "factor")),
row.names = c(NA, -4L),
class = "data.frame") %>%
# Renaming the column, so we can join the two data sets together
rename(variable = sample)
data_melt <- data_melt %>%
left_join(metadata, by = "variable")

Filter table with action button and 2nd table with crosstalk

I have a flexdashboard with 1 input select, an action button and two tables. I want the first table to be filtered using the input select, triggered by the action button. Then I want to filter a 2nd table depending on a selected row from table 1. For the latter I'm thinking of using crosstalk.
This works (using the example from: stackoverflow.com/questions/48581598/filter-two-tables-with-crosstalk):
df1 <- structure(list(owner = structure(c(1L, 2L, 2L, 2L, 2L), .Label = c ("John", "Mark"), class = "factor"), hp = c(250, 120, 250, 100, 110), car = structure(c(2L, 2L, 2L, 1L, 1L), .Label = c("benz", "bmw"), class = "factor"), id = structure(1:5, .Label = c("car1", "car2", "car3", "car4", "car5"), class = "factor")), .Names = c("owner", "hp", "car", "id"), row.names = c(NA, -5L), class = "data.frame")
df2 <- structure(list(car = structure(c(1L, 2L, 1L, 2L), .Label = c ("benz","bmw"), class = "factor"), owner = structure(c(1L, 1L, 2L, 2L), .Label = c("John", "Mark"), class = "factor"), freq = c(0L,1L, 2L, 2L)), .Names = c ("car", "owner", "freq"), row.names = c(NA, -4L), class = "data.frame")
shared_df1 <- SharedData$new(df1, ~owner, group = "Choose owner")
shared_df2 <- SharedData$new(df2, ~owner, group = "Choose owner")
filter_select("owner", "Car owner:", shared_df1, ~owner)
DT::datatable(shared_df1)
DT::datatable(shared_df2)
#---------------------------
But adding this doesn't:
selectInput(inputId = 'owner', label = NULL, choices =c("All", "John", "Mark"), selected ="All")
actionButton("Find", "Find owner ")
observeEvent(input$Find,{
df1<-df1%>%filter(a==input$owner|input$owner=="All")
})
Please help
It's not exactly clear what you want to do with the second table. As for the first, you could try creating a reactive dataframe which returns the filtered dataframe:
df1_filtered = reactive({
if(input$owner != "All"){
df1 %>% filter(owner == input$owner)
} else {
df1
}
})
df2_filtered = reactive({
if(condition for second table){
# If you wanted to filter the second owners in the first df
df2 %>% filter(owner %in% df1_filtered()$owner)
} else {
df2
}
})
observeEvent(input$Find,{
renderTable(df1_filtered())
})
But let me know what you are looking to do exactly and I can modify this. Also I'm not 100% sure the old if-else framework is best practices for reactive dataframes but it has worked well for me.

R Wide to long format for multiple variables with patterns [duplicate]

This question already has answers here:
Reshaping multiple sets of measurement columns (wide format) into single columns (long format)
(8 answers)
Closed 4 years ago.
I have a data set with a single identifier and five columns that repeat 18 times. I want to restructure the data into long format keeping the first five column headings as the column headings. Below is a sample with just two repeats:
structure(list(Response.ID = 1:2, Task = structure(c(1L, 1L), .Label = "task1", class = "factor"),
Freq = structure(c(1L, 1L), .Label = "Daily", class = "factor"),
Hours = c(3L, 2L), Value = c(10L, 8L), Mood = structure(1:2, .Label = c("Engaged",
"Neutral"), class = "factor"), Task.1 = structure(c(1L, 1L
), .Label = "task2", class = "factor"), Freq.1 = structure(c(1L,
1L), .Label = "Weekly", class = "factor"), Hours.1 = c(4L,
4L), Value.1 = c(10L, 6L), Mood.1 = structure(c(2L, 1L), .Label = c("Neutral",
"Optimistic"), class = "factor")), .Names = c("Response.ID", "Task", "Freq", "Hours", "Value", "Mood", "Task.1", "Freq.1", "Hours.1", "Value.1", "Mood.1"), class = "data.frame", row.names = c(NA, -2L))
I attempted using the melt and patterns functions, which appears to approximate my desired outcome without the desired column headings:
df = melt(df1, id.vars = c("Response.ID"), measure.vars = patterns("^Task", "^Freq","^Hours","^Mood"))
Here is the result:
structure(list(Response.ID = c(1L, 2L, 1L, 2L), variable = structure(c(1L, 1L, 2L, 2L), class = "factor", .Label = c("1", "2")), value1 = c("task1", "task1", "task2", "task2"), value2 = c("Daily", "Daily", "Weekly", "Weekly"), value3 = c(3L, 2L, 4L, 4L), value4 = c("Engaged", "Neutral", "Optimistic", "Neutral")), .Names = c("Response.ID", "variable", "value1", "value2", "value3", "value4"), row.names = c(NA, -4L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x0000000000330788>)
When I tried to specify names with value.name() below I receive an error:
df = melt(df1, id.vars = c("Response.ID"),measure.vars = patterns("^Task", "^Freq","^Hours","^Mood"), value.name=c("Task", "Freq", "Hours", "Value","Mood"))
My desired result would look like this:
structure(list(Response.ID = c(1L, 2L, 1L, 2L), Task = structure(c(1L, 1L, 2L, 2L), .Label = c("task1", "task2"), class = "factor"),
Freq = structure(c(1L, 1L, 2L, 2L), .Label = c("Daily", "Weekly"
), class = "factor"), Hours = c(3L, 2L, 4L, 4L), Value = c(10L,
8L, 10L, 6L), Mood = structure(c(1L, 2L, 3L, 2L), .Label = c("Engaged",
"Neutral", "Optimistic"), class = "factor")), .Names = c("Response.ID", "Task", "Freq", "Hours", "Value", "Mood"), class = "data.frame", row.names = c(NA, -4L))
It looks to me like you embarked on a difficult journey by using melt: this function is well named in the sense that trying to use it will probably melt your brain. Joke aside, the function melt has lots of underlying computations and its use could be inefficient if you have a large dataset.
I would instead solve the problem manually with rbindlist (from the excellent package data.table, which also ships with an optimized version of melt if you really want to use it), to manually concatenates groups of columns. This also preserves the column names:
> rbindlist(lapply(1:2, function(i) df1[,c(1,((i-1)*5+2):((i-1)*5+6))]))
Response.ID Task Freq Hours Value Mood
1: 1 task1 Daily 3 10 Engaged
2: 2 task1 Daily 2 8 Neutral
3: 1 task2 Weekly 4 10 Optimistic
4: 2 task2 Weekly 4 6 Neutral
This works on your example: replace the indices 1:2 by the number of repetitions to make it work with the real dataset (so, lapply(1:18)).

Links in table cells in R markdown pdf document

I have a table that I am trying to place in an rmarkdown pdf document. I am also trying to format the table so that the station numbers are clickable links for associated webpages.
I have created a function to transform the original table into a table with links in rmarkdown format. All works well except that the column that the links are in is so large that none of the other columns appear on the page. I have tried changing the width of the figure in the chunk details line, and changing the width of the table in the kable line with format.args= (width = 15). Neither of these options change the formatting, and I have not found any similar questions/answers online. This question: Including links within Rmarkdown tables (pdf) appears to be similar but as my table will produce a table hundreds of pages long, I am hoping to stick to a way of using the links table I have created.
The kable function:
kable(local.select, format = "markdown", row.names= FALSE, format.args= (width = 15))
The issue (very wide column width when links are added to station numbers, and all other columns are pushed off the page):
The original table:
structure(list(Station = structure(1:3, .Label = c("01AF002",
"01AP002", "01AP004"), class = "factor"), FLOW_TYPE = structure(c(1L,
1L, 1L), .Label = "Normal", class = "factor"), Reg. = structure(c(2L,
1L, 1L), .Label = c(" False", " True"), class = "factor"), OperSched = structure(c(1L,
1L, 1L), .Label = " Continuous", class = "factor"), DrainageArea = c(21900L,
668L, 1100L), PEARSEDA = structure(c(1L, 1L, 1L), .Label = "Saint John - St. Croix", class = "factor")), class = "data.frame", row.names = c(NA,
-3L), .Names = c("Station", "FLOW_TYPE", "Reg.", "OperSched",
"DrainageArea", "PEARSEDA"))
The link-converted table:
structure(list(`Station Number` = structure(1:3, .Label = c("[01AF002](http://wateroffice.ec.gc.ca/report/report_e.html?mode=Graph&type=h2oArc&stn=01AF002&dataType=Daily&parameterType=Flow&year=2013&scale=normal)",
"[01AP002](http://wateroffice.ec.gc.ca/report/report_e.html?mode=Graph&type=h2oArc&stn=01AP002&dataType=Daily&parameterType=Flow&year=2013&scale=normal)",
"[01AP004](http://wateroffice.ec.gc.ca/report/report_e.html?mode=Graph&type=h2oArc&stn=01AP004&dataType=Daily&parameterType=Flow&year=2013&scale=normal)"
), class = "factor"), `Water Quantity` = structure(c(1L, 1L,
1L), .Label = "Normal", class = "factor"), `Water Flow` = structure(c(2L,
1L, 1L), .Label = c(" False", " True"), class = "factor"), `Sampling Frequency` = structure(c(1L,
1L, 1L), .Label = " Continuous", class = "factor"), `Drainage Area` = c(21900L,
668L, 1100L), PEARSEDA = structure(c(1L, 1L, 1L), .Label = "Saint John - St. Croix", class = "factor")), .Names = c("Station Number",
"Water Quantity", "Water Flow", "Sampling Frequency", "Drainage Area",
"PEARSEDA"), class = "data.frame", row.names = c(NA, -3L))
The original table in the rmarkdown document (as I would like it to appear when there are links associated with the station names):
Is there a way to put links in a table without the extra spacing it seems to create in the links column?
Thanks for any suggestions.

Resources