I would like to create a table using knitr:kable in R where I am using several auxiliary columns for conditional formating.
The table (df_prices) looks like this:
device price competion_price
A 20 23
B 158 160
C 1000 999
I am using the mutate and cell_spec for conditional formating just like this:
df_prices%>%
mutate(price= cell_spec(
price, color = "grey", bold = T,
background = ifelse(price <= competion_price, green, red) %>%
kable(escape = F, align = "c") %>%
kable_styling(bootstrap_options = "striped", full_width = T, position = "left",font_size = 14) %>%
collapse_rows(columns = c(1), valign = "middle")
This works OK, but in the final output I would like to hide the column "competion_price" so that the output would look like this but with correct highlighting:
device price
A 20
B 158
C 1000
Is something like this possible? Thank you very much for any suggestions.
Use dplyr::select with - to de-select a column like this.
library(knitr)
library(kableExtra)
library(dplyr)
df_prices <- read.table(text="device price competion_price
A 20 23
B 158 160
C 1000 999", sep='',header=T)
df_prices %>%
dplyr::mutate(price= cell_spec(price, color = "grey", bold = T, background = ifelse(price <= competion_price, 'green', 'red'))) %>%
dplyr::select(-competion_price) %>%
kable(escape = F, align = "c") %>%
kable_styling(bootstrap_options = "striped", full_width = T, position = "left",font_size = 14) %>%
collapse_rows(columns = c(1), valign = "middle")
(there are also a couple of fixes to the original code to make it work)
You can also use kableExtra::remove_column(), to remove a selected column from your final kable table.
For example, this will remove the first column from a table
library(kableExtra)
my_table = kable(mtcars)
remove_column(my_table, 1)
Related
Hei,
To compare several variants of data I produced a HTML report.
Given a special catagory some indexes in the database should be the same. To detect errors / incorrect entries in the database I compare the different categories in a table.
For better reading, it would be fine, to have coloured tables. This can be done easily with the formattable-Package.
My dataset:
require(tidyverse)
require(formattable)
require(kableExtra)
require(knitr)
df1 <- data.frame(V1 = c(68,sample(c("J","N"),size=15,replace = TRUE)),
V2 = c(10,sample(c("J","N"),size=15,replace = TRUE)),
V3 = c(1,sample(c("J","N"),size=15,replace = TRUE))
)
It has - in this example - 3 differnt variants. Only one is recomended. It is supposed, that the variant with the highest N (=first entry in each Vx-Column) is the real one.
My formated table is produced with this code:
df1 %>%
mutate(
V2 = ifelse((as.character(V2) == as.character(V1)) == FALSE,
cell_spec(V2, color = "red",bold = TRUE),
cell_spec(V2, color = "black",bold = FALSE)),
V3 = ifelse((as.character(V3) == as.character(V1)) == FALSE,
cell_spec(V3, color = "red",bold = TRUE),
cell_spec(V3, color = "black",bold = FALSE))
) %>%
kable(format = "html", escape = FALSE) %>%
kable_styling(c("striped", "condensed"), full_width = FALSE) %>%
row_spec(1, bold = T, color = "white", background = "#D7261E")
Two questions:
How to mutate in a loop?
This is necessary because the different categories I have to investigate can have up to 18 different variants. In each dataset, V1 is everytime the reference variant.
As you can see (run the code!) the first line (the "N"s) is coded in the wrong matter. Is it possible to compare from the second line on only (first line is set to TRUE by default)
This would be fine, because the first line is now formated in a matter that does not really make sense.
Thank you!
To answer your two questions:
Instead of looping over the columns, you can use mutate_all
Just take a copy of the first column and mutate it back in later
I have first made your cell_spec calls into functions to reduce clutter in the code.
red <- function(x) cell_spec(x, color = "red", bold = TRUE)
black <- function(x) cell_spec(x, color = "black", bold = FALSE)
c1 <- as.character(df1[[1]])
Now we can do this:
df1 %>%
select(-V1) %>%
mutate_all(function(x) ifelse(as.character(x) != df1[[1]], red(x), black(x))) %>%
mutate(V1 = black(c1)) %>%
mutate_all(function(x) `[<-`(x, 1, " ")) %>%
select(V1, V2, V3) %>%
kable(format = "html", escape = FALSE) %>%
kable_styling(c("striped", "condensed"), full_width = FALSE) %>%
row_spec(1, bold = T, color = "white", background = "#D7261E")
Which gives this result:
Thank you, #AllanCameron!
I 'm not familiar to the package purrr - I really should do more studies about it.
Your idea with purrr::map_dfc solved the problem.
Instead of the first column I need the first row (the digit-row), and of course with grepl it is possible to solve this. The condition in the ifelse-Statement is a little bit longer then.
My final solution is then:
df1 %>%
map_dfc(function(x) ifelse(as.character(x) != as.character(df1$V1) & !grepl("[[:digit:]]",x),
mark_true(x), mark_false(x))) %>%
select(V1, everything()) %>%
kable(format = "html", escape = FALSE) %>%
kable_styling(c("striped", "condensed"), full_width = FALSE) %>%
row_spec(1, bold = T, color = "white", background = "#D7261E")
Thank you very much!
I'm having trouble creating a nicely formatted table in R. I'm 90% of the way there, but can't get all the way.
I need to color the entire cell with a background color as seen in the example below. I read the kable vignette and saw that in html format, background does not color the whole cell. Is there a way to get around this? I tried setting it to latex instead, but the output is in latex rather than shown in the viewer. I'm also a novice markdown user so when I tried it there, the output was not what I was hoping for (which is simply a self-contained table).
I've done tons of searching on SO for a solution, but I haven't been able to get it. It sure isn't easy to produce tables in R. Any help would be appreciated.
Sample Data:
library(tidyverse)
df <- structure(list(Indicator = c("Var1", "Var2", "Var3", "Var4", "Var5"
), Sign = c(-1L, 1L, 1L, -1L, 1L), Freq = c("M", "A", "Q", "M",
"M")), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"))
df
# A tibble: 5 x 3
Indicator Sign Freq
<chr> <int> <chr>
1 Var1 -1 M
2 Var2 1 A
3 Var3 1 Q
4 Var4 -1 M
5 Var5 1 M
Attempted code:
library(kable)
library(kableExtra)
df %>%
dplyr::rename(Trend = Freq) %>%
mutate(Indicator = cell_spec(Indicator, "html", color = "black", bold = T),
Trend = cell_spec(Trend, "html", color = "white", bold = T,
background = factor(Sign, c(-1, 0, 1),
c("red", "gray", "green")))) %>%
select(Indicator, Trend) %>%
kable(align = c('l', 'c'), format = "html", escape = F) %>%
kable_styling(bootstrap_options = c("bordered", full_width = F, font_size = 16)) %>%
row_spec(0, background = "rgb(172, 178, 152)", color = "black", font_size = 18)
I simplified the initial data to be clear:
df <- tribble(~Indicator, ~Freq, ~cellColor,
'Speed', 43.342, 'red',
'Altitude', 44.444, 'blue',
'Smartness', 0.343, 'green')
To success, we need to create the table object (tbl), because the kable library has function column_spec for the fixed column width setting.
tbl <- df %>%
mutate(Indicator = cell_spec(Indicator, "html", color = "black", bold = T),
Freq = cell_spec(x = Freq,
format = "html",
color = "white",
bold = T,
extra_css = paste(paste('background-color', cellColor, sep = ': '), # combine background-color CSS rule with the observation vector value
'display: inline-block', # extremely important CSS modifier for the span tag in the table cell
'text-align: center', # text align
'padding: 0px', # expand the field of text
'margin: 0px', # expand the field of text
'width: 200px', # future cell/column width
sep = "; "), # CSS notation rule
)
) %>%
select(-cellColor) %>% # exclude cellColor vector
kable(format = "html", escape = F) %>%
kable_styling(bootstrap_options = c("bordered", full_width = F, font_size = 16))
column_spec(tbl, 2, width = "200px") # set the column width as the cell width
tbl # print
As one can see, it is important to match the column and cell size. As an example, I made both of them 200px wide.
The result:
I'm using R version 3.6.1 in RStudio. I have flextable version 0.5.5 and officer version 0.3.5.
I'm having difficulty with formatting my numbers in flextables within RMarkdown. By default, all numbers show up with 3 decimal places. For some of my numbers, this is fine (and actually preferred), but for others, I want to remove the decimals.
Using the advice found here I was able to adjust my table so that all numbers are rounded to the nearest whole number. My code is below (example table used for reproduciblility; otherwise formatting is the same as my current code).
ft_test <- head(iris) %>% flextable() %>%
hline(part = 'header', border = fp_border(color = "black", width = 3)) %>%
align(align ='center', part = 'all') %>%
align(j = 1, align ='left', part = 'all') %>%
set_formatter_type(fmt_double = "%.0f")
ft_test
However, I only want certain columns to be whole numbers, and other columns to still have decimals. I've tried using the j argument to call certain columns:
ft_test <- head(iris) %>% flextable() %>%
hline(part = 'header', border = fp_border(color = "black", width = 3)) %>%
align(align ='center', part = 'all') %>%
align(j = 1, align ='left', part = 'all') %>%
set_formatter_type(fmt_double = "%.0f", j = 2)
ft_test
... but then I get an error telling me j = 2 is an unused argument.
Any suggestions for how to adjust the numbers of only some columns? Thanks in advance for your help!
You can not use argument j as it is not an argument of set_formatter_type. The function is setting formatters for one or several data type. In your case, it's better to use colformat_num.
library(flextable)
library(officer)
library(magrittr)
ft_test <- head(iris) %>% flextable() %>%
hline(part = 'header', border = fp_border(color = "black", width = 3)) %>%
align(align ='center', part = 'all') %>%
align(j = 1, align ='left', part = 'all') %>%
colformat_num(j = c("Sepal.Length", "Sepal.Width",
"Petal.Length", "Petal.Width"), digits = 1)
ft_test
You can learn more about formatting content here: https://davidgohel.github.io/flextable/articles/display.html
I have R Markdown scripts I run periodically which contain conditional tables with what I'll call violators. Here's an example data frame:
df <- data.frame(Person = c("Jack", "Jill"), Violator = c("F", "F"))
#> Person Violator
#> 1 Jack F
#> 2 Jill F
I only want to show violators (Violator == "T") and there aren't any this month. So my 'normal' kable code below gives me this error, "subscript out of bounds" which I'd expect.
How can I modify my kable code to 'do nothing' if violator does not equal "T". Is ifelse() the way to go? I'm open to kableExtra() solutions.
kable(df %>% filter(Violator == "T"), "html", align = "l") %>%
kable_styling("striped", "hover", full_width = F) %>%
column_spec(1, bold = T, background = "#FFFFFF") %>%
collapse_rows(columns = 1)
This simple approach should work, I think:
```{r}
temp <- df %>% filter(Violator == "T")
if(nrow(temp) != 0){
kable(temp, "html", align = "l") %>%
kable_styling("striped", "hover", full_width = F) %>%
column_spec(1, bold = T, background = "#FFFFFF") %>%
collapse_rows(columns = 1)
}
```
I am formatting a table using formattable:color_bar and would like to add a comma as a thousands separator as well as adjust the colour of the font.
For cells where I don't use color_bar I see how I can use cell_spec to change the font color but I don't know how to do it with cells that are using color_bar.
library(tidyverse)
library(knitr)
library(kableExtra)
library(formattable)
df <- tibble (
rank = c(1,2,3),
tree = c("Norway Maple", "Honey Locust", "Colorado Blue Spruce"),
number = c(74688, 24286, 21917)
)
df %>%
mutate(
tree = cell_spec(tree, "html", color = "black"),
number = color_bar()(number)) %>%
kable("html", escape = F, align = c("l", "l")) %>%
kable_styling(bootstrap_options = c("hover", "responsive", "condensed"), full_width = F, position = "float_left") %>%
column_spec(3, width = "10cm")