I am trying to make a pie chart in R using plotly. I have a tibble (df) with 4 columns - (1) an observation (x), (2) value of the observation (y), (3) category of the observation (cat), and (4) color of each observation (colors). Colors are unique for each category (every observation within the same category will share the same color). I need each segment of the pie chart to represent each observation with the size of the segment corresponding to the value of the observation. I also need the segments to be colored by their unique category color. I have been able to build such a pie chart.
I am, however, struggling with how to customize the legend. Using showlegend=TRUE shows each observation with it's color. I need the legend to showcase each unique category with its distinct color. I would really appreciate it if someone could help me out with this.
Here is some dummy code that models this problem -
# Load packages
library(tidyverse)
library(plotly)
# Initialize variables
df = NULL
x = c("apple", "John", "dog", "lion", "strawberry", "Liz",
"cat", "peach", "banana", "elephant", "pear", "tiger")
y = c(1, 1, 3, 5, 4, 3, 6, 4, 1, 2, 1, 6)
cat = c("fruit", "person", "animal", "animal", "fruit", "person",
"animal", "fruit", "fruit", "animal", "fruit", "animal")
colors = NULL
# Define colors
for (i in 1:length(cat)) {
if (cat[i] == "fruit") {
color = "#FFD700"
} else if (cat[i] == "animal") {
color = "#FB8072"
} else {
color = "#D3D3D3"
}
colors = c(colors, color)
}
# Create data frame
df = as_tibble(cbind(x, y, cat, colors))
# Sort data frame
df = df[order(df$cat, df$x), ]
# Define colors for pie chart
pie_colors = as.character(df$colors)
# Define margins for pie chart
margins = list(l=50, r=50, b=125, t=125)
# Build the pie chart
pie_plt = plot_ly(data=df,
values=~y,
labels=~x,
type="pie",
textinfo="label+percent",
sort=FALSE,
marker=list(colors=pie_colors,
line=list(color="black", width=1))) %>%
layout(autosize=FALSE, margin=margins, showlegend=TRUE)
# Show pie chart:
pie_plt
Related
I am making a gt table showing the progress of individuals towards a goal. In the table, there is a row showing a horizontal bar graph of progress towards that goal (if goal is 50 and score is 40, the bar is at 80%).
However, when I change the order of the gt rows by using the groupname_col argument, the order of the other cells changes, but not the order of the gtExtras gt_plt_bar_pct column, so it's showing the wrong bars for the name and score in that row, instead, that column seems to always be represented in the order of rows in the input data.
I understand that I can fix this by using arrange on the df before the gt begins, but this doesn't seem like a good solution since I'm going to want to change the order of the rows to view by different groups. Is this a flaw with gtExtras? is there a better fix?
thanks!
reprex:
library(tibble)
library(gt)
library(gtExtras)
library(dplyr)
# make dataframe of individuals and their goals
df <- tribble(
~name, ~group, ~score, ~goal,
"Bob", "C", 20, 40,
"Chris", "A", 50, 40,
"Dale", "B", 30, 50,
"Jay", "A", 0, 40,
"Ben", "B", 10, 20
) %>%
# calculate percent towards goal, and cap at 100%
mutate(percent_to_goal = score/goal *100,
percent_to_goal = case_when(percent_to_goal >= 100 ~ 100,
TRUE ~ percent_to_goal))
df %>%
# this fixes the issue, but doesn't seem like a permanent solution
#arrange(group, name) %>%
# make gt table
gt(rowname_col = "name", groupname_col = "group") %>%
# order groups
row_group_order(groups = c("A","B","C")) %>%
# add bar chart column
gt_plt_bar_pct(column = percent_to_goal) %>%
# highlight blue if person reaches their goal
tab_style(
style = list(
cell_fill(color = "lightcyan"),
cell_text(weight = "bold")),
locations = cells_body(
columns = c(goal,score, percent_to_goal),
rows = score >= goal
)
)
Here is the output from the above code: notice that the length of the bar charts do not always reflect the values of the rows they are appearing in. Instead, they reflect the order of the original dataset.
EDIT: remove row_group_order. If I run the above code again, but comment out the line meant to rearrange the appearance of groups, the grouping shows up in a different order (order of appearance of groups in the original dataset), and the name and first two columns sort into these groups accordingly, but the bar chart column still does not, and remains in the original order of the dataset. Image below:
Per gtExtras v 0.2.4 this bug has been fixed. Thanks for raising and the great reprex!
library(tibble)
library(gt)
library(gtExtras)
library(dplyr)
# make dataframe of individuals and their goals
df <- tribble(
~name, ~group, ~score, ~goal,
"Bob", "C", 20, 40,
"Chris", "A", 50, 40,
"Dale", "B", 30, 50,
"Jay", "A", 0, 40,
"Ben", "B", 10, 20
) %>%
# calculate percent towards goal, and cap at 100%
mutate(percent_to_goal = score/goal *100,
percent_to_goal = case_when(percent_to_goal >= 100 ~ 100,
TRUE ~ percent_to_goal))
df %>%
# make gt table
gt(rowname_col = "name", groupname_col = "group") %>%
# order groups
row_group_order(groups = c("A","B","C")) %>%
# add bar chart column
gt_plt_bar_pct(column = percent_to_goal) %>%
# highlight blue if person reaches their goal
tab_style(
style = list(
cell_fill(color = "lightcyan"),
cell_text(weight = "bold")),
locations = cells_body(
columns = c(goal,score, percent_to_goal),
rows = score >= goal
)
)
I am trying the create a plotly gauge graph for a flexdashboard which should change value depending on the chosen filter in crosstalk::filter_select().
I have tried and tried but cannot get the filter to work. This is an example with mtcars of what I am trying to do. I noticed that if the SharedData object has only one value, then it works, but otherwise plotly does not show any data.
mtcars_data <- tibble::rownames_to_column(mtcars, "Car")
shared_mtcars <- SharedData$new(mtcars_data)
row1 <- bscols(filter_select("Car", "Car", shared_mtcars, ~Car, multiple = F)
)
fig <- plot_ly(shared_mtcars,
domain = list(x = c(0, 1), y = c(0, 1)),
value = ~mpg,
title = list(text = "MPG"),
type = "indicator",
mode = "gauge+number")
bscols(row1, fig, widths = 12)
This code results in a graph with no data. If I subset mtcars_data to take the first row or the first two rows (which happen to have the same value for mpg) then it works. If I subset rows 1 and 3, it doesn't.
I might be missing something - in that case would really appreciate any feedback.
I'm creating heatmaps in R using heatmap.2 (I think it needs to be heatmap.2 because I'm using 1 dataset to generate the colours of the heatmap and a second dataset to overlay numerical data).
Here is a sample of my code so far. The actual data set is 30 columns and 1000 rows.
heatmap_all_data <-
data.frame(name = c("John", "Mark", "Luke", "Jack", "Will", "Jim", "Clive", "Steve"),
trait_1 = c(1, 2, 5, 8, 5, 3, 7, 8),
trait_2 = c(5, 7, 3, 4, 6, 3, 2, 1)) %>%
column_to_rownames(var="name")
heatmap_colour <- colorRampPalette(brewer.pal(11, "RdYlBu"))(1000)
heatmap.2(as.matrix(heatmap_all_data),
scale = "column",
key = FALSE,
dendrogram = "none",
Rowv = FALSE,
Colv = FALSE,
trace = "none",
col = rev(heatmap_colour),
labRow = row.names(heatmap_all_data))
Which generates the following heatmap: https://i.stack.imgur.com/lK8Sc.png
NOW, the problem is I only want a subsection of this data, e.g I want the following heatmap:
heatmap_part_data <-
data.frame(name = c("John", "Mark", "Luke"),
trait_1 = c(1, 2, 5),
trait_2 = c(5, 7, 3)) %>%
column_to_rownames(var="name")
heatmap_colour <- colorRampPalette(brewer.pal(11, "RdYlBu"))(1000)
heatmap.2(as.matrix(heatmap_part_data),
scale = "column",
key = FALSE,
dendrogram = "none",
Rowv = FALSE,
Colv = FALSE,
trace = "none",
col = rev(heatmap_colour),
labRow = row.names(heatmap_part_data))
https://i.stack.imgur.com/j33Ic.png
BUT, I want each cell to keep the same colours as the original. I.e. I want the colours in my subsetted heatmap to be relative to the total data and not just the subsetted data. (In the real example I want to show 10 out of 1000 entries).
So, I need to either "zoom in" and rescale the top section of the heatmap and then crop the image, extract the top section of the heatmap into a new object while maintaining the same colours, or extract information about the colours in the full heatmap and overwrite the default colours in the subsetted heatmap.
The goal is basically to output an image of the subsetted data heatmap with each colour in each cell the same as in the all_data heatmap.
I hope this is clear - please advise if you need any clarification!
Many thanks for taking the time to read and I hope someone can help.
Best,
Ryan
Found the solution!
So I switched from heatmap.2 to heatmaply - same functionality but with interactivity. With heatmaply you can drag an area over the heatmap and zoom into that area which gives the desired result but I wanted to consistently zoom to a specific area.
From this website (https://plotly.com/r/axes/) I found out about the Layout function of the wider plotly library (that heatmaply is part of).
So to the existing code you can add:
%>% layout(yaxis = list(range = c(10.5, 0.5)))
(Need to add 0.5 to centre the rows properly)
Et voila! The heatmap colours are generated relative to the wider dataset but only a subset is shown.
I wanted to have my conditions labelled on the heatmap I am making for DGE.
This code:
mat <- assay(rld)[topVarGenes,]
condition = c("black", "orange")
names(condition) = c("Dark", "Light")
ann_colors = list(condition = condition)
pheatmap(mat, color = colorRampPalette(rev(brewer.pal(n = 7, name = "RdYlBu")))(24), annotation_colors = ann_colors[1], border_color = "grey60", fontsize = 12, scale = "row")
produces this heatmap:
But, this heatmap doesn't have the conditions labelled above the columns like I wanted. So I tried this code:
annotation <- data.frame(annotation)
pheatmap(mat, annotation = annotation, color = colorRampPalette(rev(brewer.pal(n = 7, name = "RdYlBu")))(24), border_color = "grey60", fontsize = 12, scale = "row")
Which almost works, but doesn't use the colors I want to label the conditions (samples 1-3 are "dark" condition and are to be labelled black and samples 4-6 are "light" condition and are to be labelled orange). This graph also includes a funky column label under condition for sample which is redundant and I don't know how to get rid of it. Also, the data.frame(annotation) is an excel sheet I imported of samples and corresponding conditions.
Adding back the annotation_colors to the code:
pheatmap(mat, annotation = annotation, color = colorRampPalette(rev(brewer.pal(n = 7, name = "RdYlBu")))(24), annotation_colors = ann_colors, border_color = "grey60", fontsize = 12, scale = "row")
produces this error:
Error in convert_annotations(annotation_col, annotation_colors) :
Factor levels on variable condition do not match with annotation_colors
Lastly, I tried this bit of code I found in a stack overflow post to define annotation, which gets R to use the correct colors, but they are not in the correct order for the conditions because the %% 2==0 causes it to label every other sample as 'light', but I can't think of anything else to do. Here is the code:
annotation <- data.frame(condition = factor(1:6 %% 2==0, labels = c("Dark", "Light")))
Help is greatly appreciated!
It's not so clear in the vignette, but you can follow the steps below to generate the right data.frame and list, no reason not to work:
First I make a matrix like yours:
library(pheatmap)
M = cbind(matrix(runif(30,min=0,max=0.5),ncol=3),
matrix(runif(30,min=0.3,max=0.8),ncol=3))
rownames(M) = paste0("row",1:10)
colnames(M) = paste0("sample",1:6)
Let's say first 3 columns are "light", and last 3 columns are "dark". We create a data.frame for this, important thing is to have rownames that match the colnames of your matrix:
ann_column = data.frame(
condition = rep(c("light","dark"),each=3))
rownames(ann_col) = colnames(M)
ann_column
condition
1 light
2 light
3 light
4 dark
5 dark
6 dark
Now for the colors, you need a list, and the names need to match the data frame above, and inside the light, you specify what factor matches what color, so:
ann_colors = list(condition = c(dark="black",light="orange"))
And we draw it:
pheatmap(M,annotation_col=ann_col,annotation_colors=ann_colors)
I am referring to the choropleth tutorial for Leaflet (https://rstudio.github.io/leaflet/choropleths.html) and modifying it for Shiny. I have different columns that I want to be able to use depending on what the user selects. The problem I encounter has to do with this part:
pal <- colorBin("YlOrRd", domain = states$density, bins = bins)
m %>% addPolygons(
fillColor = ~pal(density),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7)
Specifically, I want to be able to replace the density column be a column that I can get from a button in Shiny (assume the columns are called a and b and that I get them from the name_button object). I create the col_name function to enclose this choice:
col_name <- reactive({
name <- switch(input$name_button, "A" = "a", "B" = "b" )
name})
Then I can modify the pal <- ... line as follows (see R how use a string variable to select a data frame column using $ notation):
pal <- colorBin("YlOrRd", domain = states[[col_name()]], bins = bins)
However, I am not sure how to change the fillColor = ~pal(density), line because density is the name of a column. I have tried
fillColor = ~pal([[col_name]])
but this doesn't work. What can I do?
Also, what is the function of the tilde ~ in ~pal(...)?