简体   繁体   English

R Plotly - 根据大小分配标记的颜色

[英]R Plotly - Assign the color of a marker by its size

I am using plotly to create an interactive pie chart for a shiny app.我正在使用 plotly 为 shiny 应用创建交互式饼图。 The underlying data is comparable to sales data.基础数据与销售数据相当。 Each observation corresponds to a sale of a product.每个观察对应于产品的销售。 The pie chart then sums up the sales by product category.然后饼图按产品类别汇总销售额。

I am looking for a way to assign color (gradient) by size of the respective pie piece, ie I would like the biggest share to have the darkest and the smallest share to have the lightest color.我正在寻找一种根据各个饼图的大小分配颜色(渐变)的方法,即我希望最大的份额具有最深的颜色,最小的份额具有最浅的颜色。 I know how to do this manually as shown in the example.我知道如何手动执行此操作,如示例所示。 However, since these shares will change over time, I am looking for a dynamic solution.但是,由于这些份额会随着时间的推移而变化,因此我正在寻找一个动态的解决方案。

This is my solution to a fixed color assignment by product category:这是我对按产品类别分配固定颜色的解决方案:

library(plotly)
library(dplyr)

Product <- c("Product1","Product1","Product1","Product2","Product2","Product2","Product3")
Value <- c(100,100,50,400,50,50,350)

df <- data.frame(Product,Value)

colors_list <- list(
  'Product2' = '#003f5c',
  'Product3' = '#444e86',
  'Product1' = '#955196')

df$color <- dplyr::recode(df$Product, !!!colors_list)


plot_ly(df, labels = ~Product, values = ~Value, type = 'pie', textinfo = 'label+percent',
        marker = list(colors = ~color,
                      line = list(color = '#FFFFFF', width = 0.5))) %>%
  layout(xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))

Here is the link to the corresponding plot这里是对应plot的链接

Thank you very much in advance.非常感谢你提前。

Edit: The solution presented by Kat works great with the working example that I provided.编辑: Kat 提出的解决方案与我提供的工作示例配合得很好。 However, when I apply it to my "real data" the colors get mixed up as shown below.但是,当我将它应用于我的“真实数据”时,colors 会混淆,如下所示。 Unfortunately, I cannot share the data as it is sensitive.不幸的是,我不能分享这些数据,因为它很敏感。 The data is imported from an excel sheet and perform some manipulations to extract numeric values from a string using gsub and as.numeric(str_trim(...)).数据从 excel 工作表导入,并执行一些操作以使用 gsub 和 as.numeric(str_trim(...)) 从字符串中提取数值。 This not a lot of information, but do you have an idea what could be the issue here?这不是很多信息,但你知道这里可能是什么问题吗?

在此处输入图像描述

I tried to make this as repeatable as possible.我试图使它尽可能可重复。 This does assume that you have only one trace and at least 1 trace (the pie plot).这确实假设您只有一条轨迹并且至少有一条轨迹(饼图)。 If so, you can you use this to identify all the colors in the gradient and assign the color map to the plot. I used the 'high' and 'low' colors you originally chose for your chart for the gradient in this function.如果是这样,您可以使用它来识别渐变中的所有 colors,并将颜色 map 分配给 plot。我使用了您最初为图表选择的“高”和“低”colors,用于此 function 中的渐变。

library(shades)
library(tidyverse)
library(plotly)

updater <- function(plt) {
  plt <- plotly_build(plt)
  if(length(plt$x$data) < 2) { # if there is only 1 trace
    df1 <- data.frame(labs = plt$x$data[[1]]$labels,
                      vals = plt$x$data[[1]]$values)
    vals <- df1 %>% group_by(labs) %>%  # find value
      summarise(perc = sum(df1$vals/sum(vals))) %>% 
      arrange(perc)            # assign colors to values/labels
    vals$colr <- gradient(c('#003f5c', '#955196'), steps = nrow(vals))
    # establish color order by data order in length of data
    df1 <- inner_join(df1, vals, by = "labs", keep = F) 
    # change the plot
    plt$x$data[[1]]$marker$colors <- df1$colr %>% unlist(use.names = F)
    plt # return the updated plot
  }
}

Here are some examples of how you might incorporate this.以下是您如何将其纳入其中的一些示例。

plot_ly(iris, labels = ~Species, values = ~Sepal.Length, type = 'pie',
        textinfo = 'label+percent') %>% 
  updater()

在此处输入图像描述

plot_ly(diamonds, labels = ~cut, values = ~price, type = "pie",
        textinfo = 'label+percent') %>% 
  updater()

在此处输入图像描述

Now when it comes to your original plot, whether you designate color or not, this will work.现在,当涉及到您的原始 plot 时,无论您是否指定color ,这都会起作用。 (It will override whatever you designate for colors.) (它将覆盖您为 colors 指定的任何内容。)

plot_ly(df, labels = ~Product, values = ~Value, type = 'pie', 
        textinfo = 'label+percent',
        marker = list(colors = ~color,
                      line = list(color = '#FFFFFF', width = 0.5))) %>%
  updater() %>% 
  layout(xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))

在此处输入图像描述

You may not be aware, but all of the arguments you designated in layout are the defaults for a pie plot. Whether you include them or not, you're getting the same plot.您可能没有意识到,您在layout中指定的所有 arguments 都是饼图 plot 的默认值。无论是否包含它们,您都将获得相同的 plot。

plot_ly(df, labels = ~Product, values = ~Value, type = 'pie', 
        textinfo = 'label+percent') %>% updater()

在此处输入图像描述

Last but not least, if you assign your plot to an object name like plt1 <- plot_ly(... , you can call this function, as well.最后但同样重要的是,如果您将 plot 分配给 object 名称,例如plt1 <- plot_ly(... ,您也可以调用它 function。

updater(plt1)         # an alternative
plt2 <- updater(plt1) # an alternative

------- Update -------- - - - - 更新 - - - -

From your comments, @Brani brought up a great point.从您的评论中,@Brani 提出了一个很好的观点。 Perhaps you could swap your real labels for letters and share some of the values you're seeing?也许您可以将您的真实标签换成字母并分享您看到的一些值? I'm not able to reproduce your issue, unfortunately.不幸的是,我无法重现您的问题。 I'm going to guess it's either an issue with negative values, a grouping issue, or both.我猜这要么是负值问题,要么是分组问题,要么两者兼而有之。

Is your data clean?你的数据干净吗? Is your label a factor?你的label是一个因素吗?

In the function updater , I group by labels ( labs ).在 function updater中,我按标签 ( labs ) 分组。 You could add print(df1) immediately following the creation of the vals data frame to find out what is getting sent to the creation of vals$colr .您可以在创建vals数据框后立即添加print(df1)以找出发送到创建vals$colr的内容。

Check out what happens with negatives.看看底片会发生什么。

set.seed(354)
dfn <- data.frame(letters = sample(
  rep(LETTERS[1:7], 10) %>% as.factor(), 50),
  vals = rnorm(50, 0, 10) %>% round(1))

plot_ly(dfn, labels = ~letters, values = ~vals, type = 'pie',
        textinfo = 'label+percent') %>% updater()

在此处输入图像描述

See how a slice is unlabeled?看看切片是如何未标记的? Where did E & C go? E&C go哪里去了? Let's take a closer look at this data.让我们仔细看看这些数据。

dfn2 <- dfn %>% group_by(letters) %>% 
  summarise(vals = sum(vals))
# # A tibble: 7 × 2
#   letters  vals
#   <fct>   <dbl>
# 1 A        25  
# 2 B         6.6
# 3 C        -5  
# 4 D        47.2
# 5 E       -29.7
# 6 F         7.7
# 7 G        19.8 

The negatives are missing.底片不见了。 That still doesn't account for why D didn't get a label.这仍然不能解释为什么 D 没有得到 label。

At the beginning of this update, I said you could add print(vals) to updater .在本次更新开始时,我说过您可以将print(vals)添加到updater中。 I added that call before running this last plot. This is what was printed in the console:我在运行最后一个 plot 之前添加了该调用。这是控制台中打印的内容:

# A tibble: 7 × 2
  labs    perc
  <fct>  <dbl>
1 C     -14.3 
2 E      -2.41
3 D       1.52
4 A       2.86
5 G       3.62
6 F       9.30
7 B      10.8 

If I add the lowest possible value (so that the lowest value is 0), see what happens.如果我添加可能的最低值(以便最低值为 0),看看会发生什么。

dfn2 <- dfn2 %>% 
  mutate(vals = vals + 29.7)

plot_ly(dfn2, labels = ~letters, values = ~vals, type = 'pie',
        textinfo = 'label+percent') %>% updater()

在此处输入图像描述

For this plot, this is what was printed in the console.对于这个 plot,这是控制台中打印的内容。

# A tibble: 7 × 2
  labs    perc
  <fct>  <dbl>
1 D       3.63
2 A       5.11
3 G       5.65
4 F       7.47
5 B       7.70
6 C      11.3 
7 E     NaN   

Since you're getting more than one slice with the same color, I'm curious about what you see if you add print .由于您得到了不止一片具有相同颜色的切片,我很好奇如果您添加print会看到什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM