In the following article, we will explore a method to add colors and styles to R DataFrames.
A few weeks ago I wrote an article about pandas dataframes
and how to assign styles, but I received messages about how to do it in R (my first love β€οΈ in languages with data) and so I decided to rewrite the article using R libraries.
So in the next section of this article, we will explore a method to add π¨colors and ποΈstyles in R DataFrames. We will focus on the application of colors and emojis, using approaches similar to the popular conditional formatting commonly used in pivot tables within spreadsheets. Through this strategy, we aim to improve the presentation of our data, making the exploration and understanding of the information not only informative but also visually attractive.
The R libraries used to create this article are as follows:
π tidyverse: Among the best, it integrates various R libraries for data manipulation, graphics, and analysis, promoting clear and efficient code.
π knitr: Automates the generation of dynamic reports.
π kableExtra: An additional extension that enhances table presentation in R Markdown documents with extra formatting options.
π reactablefrmtr: Incorporates functions to craft interactive and flexible tables in R, featuring filtering, sorting, and search functionalities.
βοΈ htmltools: Offers functions to build and manipulate HTML objects in R.
π formattable: Equipped with functions for formatting and customizing tables in R.
π flextable: Another library enabling the creation of flexible and customizable tables in R, with advanced formatting options for documents and presentations.
π ggplot2: Among the most popular R visualization libraries, it produces appealing and comprehensible graphs.
π¨ viridis: A R library for creating visually appealing color maps
These libraries empowered me to employ functions for generating HTML-style representations of DataFrames. This capability enables customization of DataFrame visual appearance during viewing.
The functions employed in this article facilitate the highlighting, coloring, and formatting of cells based on specific conditions. This makes it effortless to visually identify patterns and trends within datasets.
Next we have the code with we are going to create a pivot table using a set of data and from this you will begin to give it different styles and conditional formats such as can be seen in the previous image.
The pivot table is a tabular data structure that provides a summarized overview of information from another table, organizing the data based on one variable and displaying values associated with another variable. In this specific scenario, the pivot table organizes the data according to the βsmokerβ column and presents the total sum of tips, categorized by the days on which clients consume in the restaurant
The following example shows the pivot_table
method with the βtipsβ DataFrame
library(reshape2)
library(tidyverse)
data = tips
data_pivot <- data %>%
group_by(smoker, day) %>%
summarise(total_bill = sum(total_bill), .groups = 'drop') %>%
pivot_wider(names_from = day, values_from = total_bill)
data_pivot
# A tibble: 2 Γ 5
smoker Fri Sat Sun Thur
<fct> <dbl> <dbl> <dbl> <dbl>
1 No 73.7 885. 1169. 770.
2 Yes 252. 894. 458. 326.
In this analysis, we will use the βπ Apple Store appsβ DataFrame to explore the creation of pivot tables and customization of table styles. This dataset provides detailed insights into Apple App Store applications, covering aspects from app names to specifics like size, price, and ratings. Our objective is to efficiently break down the information while applying styles that enhance the presentation and comprehension of data effectively.
The dataset was downloaded from Kaggle and it contains more than 7000 Apple iOS mobile application details. It is important to note that the data was collected in July 2017.
column_name | column description |
---|---|
track_name | the column contains the name of the app. |
size_bytes | the column contains the size of the app in bytes. |
currency | the column contains the currency type. |
price | the column contains the price of the app. |
rating_count_tot | the column contains the total number of ratings. |
rating_count_ver | the column contains the number of ratings for the current version of the app. |
user_rating | the column contains the average user rating for the app. |
user_rating_ver | the column contains the average user rating for the current version of the app. |
ver | the column contains the current version of the app. |
cont_rating | the column contains the content rating. |
prime_genre | the column contains the primary genre. |
sup_devices.num | the column contains the number of supported devices. |
ipadSc_urls.num | the column contains the number of screenshots showed for display. |
lang.num | the column contains the number of supported languages. |
vpp_lic | the column contains the Vpp Device Based Licensing Enabled. |
In the following code chunk, we will create a DataFrame by reading the CSV file.
print(paste0("tidyverse version: ",packageVersion("tidyverse")[1]))
[1] "tidyverse version: 1.3.1"
# Create a dataframe from a csv file
# You can download the file from the following link https://github.com/r0mymendez/pandas-styles
path = 'https://raw.githubusercontent.com/r0mymendez/pandas-styles/main/data/AppleStore.csv'
data = read_delim(path , delim = ";")
In the next step, our goal is to generate a dynamic table from a Dataframe, in which the top 15 genres with the largest number of applications are filtered.
# Pivot table
# filter the data to keep only the top 15 genres
top_genre = data %>%
group_by(prime_genre) %>%
summarise(count = n(), .groups = 'drop') %>%
arrange(desc(count)) %>%
head(n = 15) %>%
pull(prime_genre)
tmp = data %>%
filter(prime_genre %in% top_genre) %>%
select(prime_genre, user_rating, price)
# create a new column with the rating rounded to the nearest integer
tmp$user_rating = paste0("rating_", as.character(trunc(tmp$user_rating)))
# create a pivot table
tmp_pivot <- tmp %>%
group_by(prime_genre, user_rating) %>%
summarise(price = mean(price, na.rm = TRUE), .groups = 'drop') %>%
pivot_wider(names_from = user_rating, values_from = price, values_fill = 0) %>%
mutate(across(where(is.numeric), ~round(., 2)))
# print the pivot table
tmp_pivot
# A tibble: 15 Γ 7
prime_genre rating_0 rating_1 rating_2 rating_3 rating_4 rating_5
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Book 0.49 0 5.32 1.66 3.04 1.92
2 Education 3.42 1.79 1.95 2.32 5.2 3.12
3 Entertainment 0.51 1.99 0.78 0.9 0.95 1.03
4 Finance 0.3 0 0 0.72 0.53 0.5
5 Games 0.85 0.84 1.21 1.71 1.52 1.29
6 Health & Fit⦠1.33 3.24 1.5 1.21 2.15 1.83
7 Lifestyle 0.29 1.27 0.81 0.9 1.08 1.37
8 Music 2.74 0 0 2.08 5.27 13.2
9 Photo & Video 0.75 0.74 1.36 2.16 1.47 1.33
10 Productivity 0.66 2.49 0.99 4.9 4.73 2.61
11 Shopping 0 0 0 0 0.03 0
12 Social Netwo⦠0.12 0.4 0.66 0.33 0.41 0.2
13 Sports 0.92 2 0.68 0.61 1.25 1.66
14 Travel 1.1 0 1.28 0.28 1.53 0.2
15 Utilities 2.03 2.49 1.25 1.67 1.65 0.66
Now we will explore the functions of the aforementioned libraries that will allow us to improve the visual presentation of DataFrames. This functionality provides us with different options to modify the appearance of the data, allowing us to customize aspects such as:
In this section, we will apply styles to both the titles and the table. Therefore we use background colors to highlight the headers and the rest of the table.
library(knitr)
library(kableExtra)
kable(tmp_pivot, "html") %>%
kable_styling("striped", full_width = F) %>%
row_spec(0, background = "#5E17EB", color = "white")
prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.20 | 3.12 |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
Health & Fitness | 1.33 | 3.24 | 1.50 | 1.21 | 2.15 | 1.83 |
Lifestyle | 0.29 | 1.27 | 0.81 | 0.90 | 1.08 | 1.37 |
Music | 2.74 | 0.00 | 0.00 | 2.08 | 5.27 | 13.16 |
Photo & Video | 0.75 | 0.74 | 1.36 | 2.16 | 1.47 | 1.33 |
Productivity | 0.66 | 2.49 | 0.99 | 4.90 | 4.73 | 2.61 |
Shopping | 0.00 | 0.00 | 0.00 | 0.00 | 0.03 | 0.00 |
Social Networking | 0.12 | 0.40 | 0.66 | 0.33 | 0.41 | 0.20 |
Sports | 0.92 | 2.00 | 0.68 | 0.61 | 1.25 | 1.66 |
Travel | 1.10 | 0.00 | 1.28 | 0.28 | 1.53 | 0.20 |
Utilities | 2.03 | 2.49 | 1.25 | 1.67 | 1.65 | 0.66 |
In following code snippet illustrates how to set a custom background color for all the rows in our DataFrame.
kable(tmp_pivot, "html") %>%
kable_styling("striped", full_width = F) %>%
row_spec(0, background = "#5E17EB", color = "white") %>%
column_spec(1, column=1:ncol(tmp_pivot) ,background = "#ECE3FF", color = "black")
prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.20 | 3.12 |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
Health & Fitness | 1.33 | 3.24 | 1.50 | 1.21 | 2.15 | 1.83 |
Lifestyle | 0.29 | 1.27 | 0.81 | 0.90 | 1.08 | 1.37 |
Music | 2.74 | 0.00 | 0.00 | 2.08 | 5.27 | 13.16 |
Photo & Video | 0.75 | 0.74 | 1.36 | 2.16 | 1.47 | 1.33 |
Productivity | 0.66 | 2.49 | 0.99 | 4.90 | 4.73 | 2.61 |
Shopping | 0.00 | 0.00 | 0.00 | 0.00 | 0.03 | 0.00 |
Social Networking | 0.12 | 0.40 | 0.66 | 0.33 | 0.41 | 0.20 |
Sports | 0.92 | 2.00 | 0.68 | 0.61 | 1.25 | 1.66 |
Travel | 1.10 | 0.00 | 1.28 | 0.28 | 1.53 | 0.20 |
Utilities | 2.03 | 2.49 | 1.25 | 1.67 | 1.65 | 0.66 |
In following code snippet illustrates how to set a custom background color for a particular cell in our DataFrame using pandas styling.
value = 4
tmp_pivot %>%
mutate(
rating_4 = cell_spec(rating_4, "html",
background = if_else(tmp_pivot$rating_4>value, "#FD636B", "#ECE3FF"),
color = if_else(tmp_pivot$rating_4>value, "white", "black")
)
) %>%
kable(format = "html", escape = F) %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, background = "#5E17EB", color = "white") %>%
column_spec(1:ncol(tmp_pivot), background = "#ECE3FF", color = "black")
prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.2 | 3.12 |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
Health & Fitness | 1.33 | 3.24 | 1.50 | 1.21 | 2.15 | 1.83 |
Lifestyle | 0.29 | 1.27 | 0.81 | 0.90 | 1.08 | 1.37 |
Music | 2.74 | 0.00 | 0.00 | 2.08 | 5.27 | 13.16 |
Photo & Video | 0.75 | 0.74 | 1.36 | 2.16 | 1.47 | 1.33 |
Productivity | 0.66 | 2.49 | 0.99 | 4.90 | 4.73 | 2.61 |
Shopping | 0.00 | 0.00 | 0.00 | 0.00 | 0.03 | 0.00 |
Social Networking | 0.12 | 0.40 | 0.66 | 0.33 | 0.41 | 0.20 |
Sports | 0.92 | 2.00 | 0.68 | 0.61 | 1.25 | 1.66 |
Travel | 1.10 | 0.00 | 1.28 | 0.28 | 1.53 | 0.20 |
Utilities | 2.03 | 2.49 | 1.25 | 1.67 | 1.65 | 0.66 |
Now, we will focus on highlighting the maximum and minimum values in our DataFrame. For this reason, we will assign distinctive background colors to these extreme values, facilitating a quicker and more intuitive understanding of the dataset. The code snippet below demonstrates how to implement this stylistic enhancement.
rating_columns <- grep("^rating", names(tmp_pivot), value = TRUE)
max_value <- max(unlist(tmp_pivot %>% select(rating_columns), use.names = FALSE))
min_value <- min(unlist(tmp_pivot %>% select(rating_columns), use.names = FALSE))
# The next function to apply specific formatting and preserve the original
format_spec <- function(x) {
if_else(x == max_value, sprintf("%.2f", x),
if_else(x == min_value, sprintf("%.2f", x),
sprintf("%.2f", x)))
}
tmp_pivot %>%
mutate(
across(rating_columns,
~ cell_spec(format_spec(.x),
"html",
background = if_else(. == max_value, "#3BE8B0",
if_else(. == min_value, "#FF66C4", "#ECE3FF")),
bold = if_else(. == max_value, TRUE,if_else(. == min_value, TRUE, FALSE))
)
)
) %>%
kable(format = "html", escape = F) %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, background = "#5E17EB", color = "white") %>%
column_spec(1:ncol(tmp_pivot), background = "#ECE3FF", color = "black")
prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.20 | 3.12 |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
Health & Fitness | 1.33 | 3.24 | 1.50 | 1.21 | 2.15 | 1.83 |
Lifestyle | 0.29 | 1.27 | 0.81 | 0.90 | 1.08 | 1.37 |
Music | 2.74 | 0.00 | 0.00 | 2.08 | 5.27 | 13.16 |
Photo & Video | 0.75 | 0.74 | 1.36 | 2.16 | 1.47 | 1.33 |
Productivity | 0.66 | 2.49 | 0.99 | 4.90 | 4.73 | 2.61 |
Shopping | 0.00 | 0.00 | 0.00 | 0.00 | 0.03 | 0.00 |
Social Networking | 0.12 | 0.40 | 0.66 | 0.33 | 0.41 | 0.20 |
Sports | 0.92 | 2.00 | 0.68 | 0.61 | 1.25 | 1.66 |
Travel | 1.10 | 0.00 | 1.28 | 0.28 | 1.53 | 0.20 |
Utilities | 2.03 | 2.49 | 1.25 | 1.67 | 1.65 | 0.66 |
In the upcoming section, we will delve into the concept of color maps, representing a spectrum of colors arranged in a gradient. A colormap, essentially a palette of colors, consists of distinctive denominations, with the most popular ones being [βviridis,β βmagma,β βinferno,β βplasmaβ, βcividisβ].
The primary objective behind creating these color spectrums is to enhance the visual representation of data. Each color in the gradient carries specific nuances, contributing to a more nuanced data visualization experience.
# Reference of the following code: https://bookdown.org/hneth/ds4psy/D-4-apx-colors-pkgs.html
n <- 10 # number of colors
# define 5 different color scales (n colors each):
v1 <- viridis(n)
v2 <- magma(n)
v3 <- inferno(n)
v4 <- plasma(n)
v5 <- cividis(n)
# See and compare color scales:
seecol(list(v1, v2, v3, v4, v5),
col_brd = "white", lwd_brd = 4,
title = "Various viridis color palettes (n = 10)",
pal_names = c("v1: viridis", "v2: magma", "v3: inferno", "v4: plasma", "v5: cividis"))
Now, we will apply a color gradient to our pivot table, allowing you to observe how it is colored using the Viridis palette. In this context, lighter colors signify larger values within the distribution, while darker shades correspond to smaller values in the distribution. This approach provides a visual representation that intuitively conveys the magnitude of the data, making it easier to discern patterns and variations across the dataset.
library(ggplot2)
# NΓΊmero de tonos (lut)
lut <- 10
# Crear un data frame con una variable continua
data <- data.frame(x = seq(1, lut))
options(repr.plot.width = 5, repr.plot.height =2)
# Crear un grΓ‘fico de barras con colores de la paleta "viridis"
ggplot(data, aes(x = x, y = 0.2, fill = as.factor(x))) +
geom_tile() +
scale_fill_manual(values = viridis(lut, option = "D")) +
labs(x = "Γndice", y = "") +
theme_void() +
theme(legend.position = "none")
# Calculate maximum and minimum values
max_value <- max(unlist(tmp_pivot %>% select(rating_columns), use.names = FALSE))
min_value <- min(unlist(tmp_pivot %>% select(rating_columns), use.names = FALSE))
# Define the number of cuts for the "viridis" palette
num_cuts <-nrow(tmp_pivot)
xc <- seq(min_value, max_value, length.out = num_cuts)
pal <- viridis(num_cuts)
# Apply color gradients to each cell with viridis
styled_table <- map(tmp_pivot, function(col) {
if (is.numeric(col)) {
cell_spec( format_spec(col),
"html",
background = pal[cut(col, breaks = xc, include.lowest = TRUE)])
} else {
cell_spec(col, "html")
}
}) %>%
as.data.frame() %>%
kable(format = "html", escape = F) %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, background = "#440154FF", color = "white") %>%
column_spec(2:ncol(tmp_pivot), color = "white") %>%
column_spec(1:1, background = "#ECE3FF")
styled_table
prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.20 | 3.12 |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
Health & Fitness | 1.33 | 3.24 | 1.50 | 1.21 | 2.15 | 1.83 |
Lifestyle | 0.29 | 1.27 | 0.81 | 0.90 | 1.08 | 1.37 |
Music | 2.74 | 0.00 | 0.00 | 2.08 | 5.27 | 13.16 |
Photo & Video | 0.75 | 0.74 | 1.36 | 2.16 | 1.47 | 1.33 |
Productivity | 0.66 | 2.49 | 0.99 | 4.90 | 4.73 | 2.61 |
Shopping | 0.00 | 0.00 | 0.00 | 0.00 | 0.03 | 0.00 |
Social Networking | 0.12 | 0.40 | 0.66 | 0.33 | 0.41 | 0.20 |
Sports | 0.92 | 2.00 | 0.68 | 0.61 | 1.25 | 1.66 |
Travel | 1.10 | 0.00 | 1.28 | 0.28 | 1.53 | 0.20 |
Utilities | 2.03 | 2.49 | 1.25 | 1.67 | 1.65 | 0.66 |
In the next code chunk, we will enhance the visual representation of our pivot table by introducing distinct color backgrounds to specific columns. This technique aids in better highlighting and categorizing data, making it easier to draw insights from the table.
tmp_pivot%>%
kable(format = "html", escape = F) %>%
kable_styling("striped", full_width = FALSE) %>%
column_spec(2:3, background = "#FFCFC9", color = "black") %>%
column_spec(4:5, background = "#FFF1B0", color = "black") %>%
column_spec(6:7, background = "#BEEAE5", color = "black")%>%
row_spec(0, background = "#440154FF", color = "white") %>%
column_spec(1:1, background = "#ECE3FF")
prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.20 | 3.12 |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
Health & Fitness | 1.33 | 3.24 | 1.50 | 1.21 | 2.15 | 1.83 |
Lifestyle | 0.29 | 1.27 | 0.81 | 0.90 | 1.08 | 1.37 |
Music | 2.74 | 0.00 | 0.00 | 2.08 | 5.27 | 13.16 |
Photo & Video | 0.75 | 0.74 | 1.36 | 2.16 | 1.47 | 1.33 |
Productivity | 0.66 | 2.49 | 0.99 | 4.90 | 4.73 | 2.61 |
Shopping | 0.00 | 0.00 | 0.00 | 0.00 | 0.03 | 0.00 |
Social Networking | 0.12 | 0.40 | 0.66 | 0.33 | 0.41 | 0.20 |
Sports | 0.92 | 2.00 | 0.68 | 0.61 | 1.25 | 1.66 |
Travel | 1.10 | 0.00 | 1.28 | 0.28 | 1.53 | 0.20 |
Utilities | 2.03 | 2.49 | 1.25 | 1.67 | 1.65 | 0.66 |
In the next code chunk, we will enhance the visual representation of our pivot table by introducing distinct color backgrounds by rows.
n <- nrow(tmp_pivot)
row_0 <-c()
row_1 <-c()
for (item in seq(1, n)) {
if (item %% 2 == 0) {
row_0 <- c(row_0,item)
} else {
row_1 <- c(row_1,item)
}
}
tmp_pivot %>%
kable(format = "html", escape = F) %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(row_0, background = "#ECE3FF", color = "black") %>%
row_spec(row_1, background = "#ffdefa", color = "black")%>%
row_spec(0, background = "#440154FF", color = "white")
prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.20 | 3.12 |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
Health & Fitness | 1.33 | 3.24 | 1.50 | 1.21 | 2.15 | 1.83 |
Lifestyle | 0.29 | 1.27 | 0.81 | 0.90 | 1.08 | 1.37 |
Music | 2.74 | 0.00 | 0.00 | 2.08 | 5.27 | 13.16 |
Photo & Video | 0.75 | 0.74 | 1.36 | 2.16 | 1.47 | 1.33 |
Productivity | 0.66 | 2.49 | 0.99 | 4.90 | 4.73 | 2.61 |
Shopping | 0.00 | 0.00 | 0.00 | 0.00 | 0.03 | 0.00 |
Social Networking | 0.12 | 0.40 | 0.66 | 0.33 | 0.41 | 0.20 |
Sports | 0.92 | 2.00 | 0.68 | 0.61 | 1.25 | 1.66 |
Travel | 1.10 | 0.00 | 1.28 | 0.28 | 1.53 | 0.20 |
Utilities | 2.03 | 2.49 | 1.25 | 1.67 | 1.65 | 0.66 |
In this section, we will implement the style.bar function to introduce a dynamic color bar into our DataFrame. The color bar provides a visual representation of data values, assigning varying colors to different data ranges.
library(htmlwidgets)
library(htmltools)
library(formattable)
formattable(tmp_pivot, list(
rating_0 = color_bar("#FFCFC9"),
rating_1 = color_bar("#FFCFC9"),
rating_2 = color_bar("#FFF1B0"),
rating_3 = color_bar("#FFF1B0"),
rating_4 = color_bar("#BEEAE5"),
rating_5 = color_bar("#BEEAE5")
)) %>%
as.htmlwidget() %>%
prependContent(tags$style("th { padding: 0px !important; background: #5E17EB; color: white }")) %>%
prependContent(tags$style("table tr td:first-child { background-color: #ECE3FF }"))
In this section, we explore the enhancement of data representation by adding an image to an additional column. This approach provides an alternative method to elevate the visual impact of the data being presented. These images can serve as icons, represent brands, or convey additional visual elements to captivate and engage the audience.
library(flextable)
flextable(tmp_pivot%>%
head(5)%>%
mutate(id=0:4,
id=paste0('img/img_',id,'.png'))%>%
select(c('id',names(tmp_pivot)))
) %>%
colformat_image(j = "id", width = .5, height = 0.5) %>%
bg(part = "header", bg = "#5E17EB", j = c('id',names(tmp_pivot)) ) %>%
color(color = "white", part = "header") %>%
bg(part = "body", bg = "#ECE3FF")
id | prime_genre | rating_0 | rating_1 | rating_2 | rating_3 | rating_4 | rating_5 |
---|---|---|---|---|---|---|---|
Book | 0.49 | 0.00 | 5.32 | 1.66 | 3.04 | 1.92 | |
Education | 3.42 | 1.79 | 1.95 | 2.32 | 5.20 | 3.12 | |
Entertainment | 0.51 | 1.99 | 0.78 | 0.90 | 0.95 | 1.03 | |
Finance | 0.30 | 0.00 | 0.00 | 0.72 | 0.53 | 0.50 | |
Games | 0.85 | 0.84 | 1.21 | 1.71 | 1.52 | 1.29 |
In this section, weβll explore a creative approach using icons and charts to visually represent comparisons between two or more columns of data.
# Define the column names
column_names <- paste0("rating_", 0:5)
# Apply the formatter function to each column
formatters_list <- lapply(column_names, function(col) {
prev_col <- paste0("rating_", as.numeric(str_extract(col, "\\d+")) - 1)
if (col == "rating_0") {
formatter(
"span",
style = ~ formattable::style("font.weight" = "bold"),
~icontext("arrow-right", tmp_pivot[[col]])
)
} else {
formatter(
"span",
style = ~ formattable::style(color = ifelse(tmp_pivot[[col]] > tmp_pivot[[prev_col]],
"green", "red"),
"font.weight" = "bold"),
~icontext(ifelse(tmp_pivot[[col]] > tmp_pivot[[prev_col]], "arrow-up", "arrow-down"), tmp_pivot[[col]])
)
}
})
# Create a named list for formattable
formatters_list <- setNames(formatters_list, column_names)
# Apply formattable and convert to htmlwidget
formattable(tmp_pivot, formatters_list) %>%
as.htmlwidget() %>%
prependContent(
tags$style("th { padding: 0px !important; background: #5E17EB; color: white }")) %>%
prependContent(
tags$style("table tr td:first-child { background-color: #ECE3FF }"))
# Get the names of columns that start with 'rating'
column_str = names(tmp_pivot[startsWith(names(tmp_pivot), "rating")])
# Get the names of columns that we will expect to have
expected_columns = c(names(tmp_pivot), 'plot_line', 'plot_bar')
# Create the new data frame with two new columns with the charts representation
tmp_nest <- tmp_pivot %>%
nest(values = c(column_str), values_1=c(column_str)) %>%
mutate( values = map(values, ~ as.numeric(.x) ) ) %>%
unnest(values_1)%>%
mutate(
plot_line = map_chr(values,
~ as.character(htmltools::as.tags(sparkline::sparkline(.x,
type = "line",
lineColor='#5E17EB',
fillColor='#5E17EB'))
)
),
plot_bar = map_chr(values,
~ as.character(htmltools::as.tags(sparkline::sparkline(.x,
type = "bar",
barColor = '#5E17EB'))
)
)
)%>%
select(expected_columns)
# Print the table with html format
out = as.htmlwidget(formattable(tmp_nest))
out$dependencies = c(out$dependencies, htmlwidgets:::widget_dependencies("sparkline", "sparkline"))
out %>%
prependContent(tags$style("th { padding: 0px !important; background: #5E17EB; color: white }")) %>%
prependContent(tags$style("table tr td:first-child { background-color: #ECE3FF }"))
library(reactablefmtr)
reactable(tmp_pivot%>%head(10),
defaultColDef = colDef(
cell = data_bars(
tmp_pivot,
box_shadow = TRUE,
round_edges = TRUE,
text_position = "outside-base",
fill_color = c("#5E17EB", "#ECE3FF"),
background = "#e5e5e5",
fill_gradient = TRUE
),
style = list(
#backgroundColor = "#ECE3FF",
color = "black",
fontSize = "14px"
),
headerStyle = list(
backgroundColor = "#5E17EB",
color = "white",
fontFamily = "Comic Sans MS",
fontSize = "14px"
)
)
)
reactable(
tmp_pivot%>%head(5)%>%select(-c('rating_0')),
defaultColDef = colDef(
align = 'center',
cell = bubble_grid(
data = tmp_pivot%>%head(5),
shape = "circles",
number_fmt = scales::comma,
colors = c("#D6B1FB","#C687FC","#A060FF"),
brighten_text=TRUE,
text_size=14,
box_shadow = TRUE,
opacity = 0.6
),
headerStyle = list(
backgroundColor = "#5E17EB",
color = "white",
fontFamily = "Comic Sans MS",
fontSize = "14px"
)
),
columns = list(
prime_genre = colDef(
style = list(
fontSize = "14px"
)
)
)
)%>%
add_title(
title = reactablefmtr::html("π Apple Store apps"),
margin = reactablefmtr::margin(t=0,r=0,b=5,l=0)
)
tmp_pivot_5 = tmp_pivot%>%head(5)
reactable(
tmp_pivot_5%>%select(-c('rating_0')),
defaultColDef = colDef(
align = 'center',
cell = bubble_grid(
data = tmp_pivot_5,
shape = "squares",
number_fmt = scales::comma,
colors = c("#D6B1FB","#C687FC","#A060FF"),
brighten_text=TRUE,
text_size=14,
box_shadow = TRUE,
opacity = 0.6,
),
headerStyle = list(
backgroundColor = "#5E17EB",
color = "white",
fontFamily = "Comic Sans MS",
fontSize = "14px"
)
),
columns = list(
prime_genre = colDef(
minWidth = 175,
cell = pill_buttons(data = tmp_pivot_5$prime_genre, box_shadow = TRUE,colors="#A060FF",text_color='white'),
style = list(
fontSize = "14px"
)
)
)
) %>%
add_title(
title = reactablefmtr::html("π Apple Store apps"),
margin = reactablefmtr::margin(t=0,r=0,b=5,l=0)
)
reactable(
tmp_pivot%>%head(5)%>%select(-c('rating_0')),,
defaultColDef = colDef(
align = 'left',
cell = gauge_chart(
data = tmp_pivot_5,
number_fmt = scales::comma,
fill_color="#5E17EB",
background="#e5e5e5",
),
headerStyle = list(
backgroundColor = "#5E17EB",
color = "white",
fontFamily = "Comic Sans MS",
fontSize = "14px"
)
),
columns = list(
prime_genre = colDef(
minWidth = 175,
header = htmltools::tags$div(
"Prime Genre",
style = list(
backgroundColor = "#5E17EB",
color = "white",
fontFamily = "Comic Sans MS",
fontSize = "14px"
)
),
cell = pill_buttons(data = tmp_pivot_5$prime_genre, box_shadow = TRUE,colors="#A060FF",text_color='white'),
style = list(
fontSize = "14px",
fontFamily = "Comic Sans MS"
)
)
)
) %>%
add_title(
title = htmltools::HTML(
sprintf("<div style='font-family:Comic Sans MS;'>%s</div>", "π Apple Store apps")
),
margin = reactablefmtr::margin(t=0,r=0,b=5,l=0)
)
In this section, we delve into the creative use of emojis based on percentile values, offering a distinctive approach to elevate data representation. By incorporating diverse emojis, we enhance the visual impact of the data. Specifically, we employ circles and squads as emojis to bring nuanced expressions to our data points.
If youβd like to view the code for creating this style, itβs available in my GitHub repository.
Feel free to check it out and give it a star if you find it helpful! βοΈ
create_series <- function(row_data, emoji) {
if (emoji == 'max') {
return(ifelse(row_data == max(row_data), 'π©', 'β¬'))
} else if (emoji == 'min') {
return(ifelse(row_data == min(row_data), 'π₯', 'β¬'))
} else if (emoji == 'min_max') {
return(ifelse(row_data == min(row_data), 'π₯',
ifelse(row_data == max(row_data), 'π©', 'β¬')))
}
}
get_percentiles <- function(row_data, bins=3, emoji='circle') {
emoji_labels <- list(
'circle' = list('3' = c('π΄', 'π‘', 'π’'),
'4' = c('π΄', 'π ', 'π‘', 'π’')),
'squad' = list('3' = c('π₯', 'π¨', 'π©'),
'4' = c('π₯', 'π¨', 'π§', 'π©'))
)
if (emoji %in% c('max', 'min', 'min_max')) {
return(create_series(row_data, emoji))
} else if (emoji %in% names(emoji_labels) & bins %in% names(emoji_labels[[emoji]])) {
labels <- emoji_labels[[emoji]][[as.character(bins)]]
return(cut(row_data, breaks=length(labels), labels=labels, ordered_result=FALSE))
} else {
return(row_data)
}
}
get_conditional_table_column <- function(data, bins=3, emoji='circle') {
tmp <- data
for (column in colnames(data)) {
if (is.numeric(data[[column]])) {
row_data_emoji <- as.character(get_percentiles(data[[column]], bins, emoji))
tmp[[column]] <- paste(format(round(data[[column]], 2), nsmall = 2), row_data_emoji)
}
}
return(tmp)
}
get_conditional_table_row <- function(data, bins=3, emoji='circle') {
response_values <- list()
column_str <- names(data)[sapply(data, is.character)]
columns_num <- names(data)[sapply(data, is.numeric)]
for (row in 1:nrow(data)) {
row_data <- data[row, columns_num]
percentil <- get_percentiles(row_data, bins, emoji)
row_data <- sapply(round(row_data, 2),format_spec)
percentil_values <- paste(row_data, percentil)
response_values <- append(response_values, list(percentil_values))
}
result_df <- data.frame(matrix(unlist(response_values),
nrow=length(response_values), byrow=TRUE))
names(result_df) <- columns_num
result_df <- cbind(data[column_str], result_df)
return(result_df)
}
# get conditional table by column with 3 bins
get_conditional_table_row(data=tmp_pivot%>%head(5),emoji='min_max')
prime_genre rating_0 rating_1 rating_2 rating_3 rating_4 rating_5
1 Book 0.49 β¬ 0.00 π₯ 5.32 π© 1.66 β¬ 3.04 β¬ 1.92 β¬
2 Education 3.42 β¬ 1.79 π₯ 1.95 β¬ 2.32 β¬ 5.20 π© 3.12 β¬
3 Entertainment 0.51 π₯ 1.99 π© 0.78 β¬ 0.90 β¬ 0.95 β¬ 1.03 β¬
4 Finance 0.30 β¬ 0.00 π₯ 0.00 π₯ 0.72 π© 0.53 β¬ 0.50 β¬
5 Games 0.85 β¬ 0.84 π₯ 1.21 β¬ 1.71 π© 1.52 β¬ 1.29 β¬
# get conditional table by column using the max value
get_conditional_table_column(data=tmp_pivot_5,emoji='max')
# A tibble: 5 Γ 7
prime_genre rating_0 rating_1 rating_2 rating_3 rating_4 rating_5
<chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 Book 0.49 β¬ 0.00 β¬ 5.32 π© 1.66 β¬ 3.04 β¬ 1.92 β¬
2 Education 3.42 π© 1.79 β¬ 1.95 β¬ 2.32 π© 5.20 π© 3.12 π©
3 Entertainment 0.51 β¬ 1.99 π© 0.78 β¬ 0.90 β¬ 0.95 β¬ 1.03 β¬
4 Finance 0.30 β¬ 0.00 β¬ 0.00 β¬ 0.72 β¬ 0.53 β¬ 0.50 β¬
5 Games 0.85 β¬ 0.84 β¬ 1.21 β¬ 1.71 β¬ 1.52 β¬ 1.29 β¬
# get conditional table by column using the circle emoji with 4 bins
get_conditional_table_column(data=tmp_pivot_5,emoji='circle',bins=4)
# A tibble: 5 Γ 7
prime_genre rating_0 rating_1 rating_2 rating_3 rating_4 rating_5
<chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 Book 0.49 π΄ 0.00 π΄ 5.32 π’ 1.66 π‘ 3.04 π‘ 1.92 π‘
2 Education 3.42 π’ 1.79 π’ 1.95 π 2.32 π’ 5.20 π’ 3.12 π’
3 Entertainment 0.51 π΄ 1.99 π’ 0.78 π΄ 0.90 π΄ 0.95 π΄ 1.03 π΄
4 Finance 0.30 π΄ 0.00 π΄ 0.00 π΄ 0.72 π΄ 0.53 π΄ 0.50 π΄
5 Games 0.85 π΄ 0.84 π 1.21 π΄ 1.71 π‘ 1.52 π΄ 1.29 π
If you want to learnβ¦
Other references:
Text and figures are licensed under Creative Commons Attribution CC BY 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".
For attribution, please cite this work as
Mendez (2024, Jan. 21). Romina Mendez: Transform your R Dataframes: Styles, π¨ Colors, and π Emojis . Retrieved from https://r0mymendez.github.io/posts_en/2024-01-14-transform-your-pandas-dataframes-in-r/
BibTeX citation
@misc{mendez2024transform, author = {Mendez, Romina}, title = {Romina Mendez: Transform your R Dataframes: Styles, π¨ Colors, and π Emojis }, url = {https://r0mymendez.github.io/posts_en/2024-01-14-transform-your-pandas-dataframes-in-r/}, year = {2024} }