【问题标题】:Using apply functions with ggplot to plot a subset of dataframe columns使用带有 ggplot 的应用函数来绘制数据框列的子集
【发布时间】:2018-05-08 07:03:29
【问题描述】:

我有一个数据框df 有很多列... 我想要列子集的图,其中c 是我要绘制的列的列表。

我目前正在做以下事情

df <-structure(list(Image.Name = structure(1:5, .Label = c("D1C1", "D2C2", "D4C1", "D5C3", "D6C2"), class = "factor"), Experiment = structure(1:5, .Label = c("020718 perfusion EPC_BC_HCT115_Day 5", "020718 perfusion EPC_BC_HCT115_Day 6", "020718 perfusion EPC_BC_HCT115_Day 7", "020718 perfusion EPC_BC_HCT115_Day 8", "020718 perfusion EPC_BC_HCT115_Day 9"), class = "factor"), Type = structure(c(2L, 1L, 1L, 2L, 1L), .Label = c("VMO", "VMT"), class = "factor"), Date = structure(c(1L, 1L, 1L, 1L, 1L), .Label = "18-Apr-18", class = "factor"), Time = structure(1:5, .Label = c("12:42:02 PM", "12:42:29 PM", "12:42:53 PM", "12:43:44 PM", "12:44:23 PM"), class = "factor"),     Low.Threshold = c(10L, 10L, 10L, 10L, 10L), High.Threshold = c(255L,     255L, 255L, 255L, 255L), Vessel.Thickness = c(7L, 7L, 7L,     7L, 7L), Small.Particles = c(0L, 0L, 0L, 0L, 0L), Fill.Holes = c(0L,     0L, 0L, 0L, 0L), Scaling.factor = c(0.001333333, 0.001333333,     0.001333333, 0.001333333, 0.001333333), X = c(NA, NA, NA,     NA, NA), Explant.area = c(1.465629333, 1.093447111, 1.014612444,     1.166950222, 1.262710222), Vessels.area = c(0.255562667,     0.185208889, 0.195792, 0.153907556, 0.227996444), Vessels.percentage.area = c(17.43706003,     16.93807474, 19.29722044, 13.18887067, 18.05611774), Total.Number.of.Junctions = c(56L,     32L, 39L, 18L, 46L), Junctions.density = c(38.20884225, 29.26524719,     38.43832215, 15.42482246, 36.42957758), Total.Vessels.Length = c(12.19494843,     9.545333135, 10.2007416, 7.686755647, 11.94211976), Average.Vessels.Length = c(0.182014156,     0.153956986, 0.188902622, 0.08938088, 0.183724919), Total.Number.of.End.Points = c(187L,     153L, 145L, 188L, 167L), Average.Lacunarity = c(0.722820111,     0.919723402, 0.86403871, 1.115896082, 0.821753818)), .Names = c("Image.Name", "Experiment", "Type", "Date", "Time", "Low.Threshold", "High.Threshold", "Vessel.Thickness", "Small.Particles", "Fill.Holes", "Scaling.factor", "X", "Explant.area", "Vessels.area", "Vessels.percentage.area", "Total.Number.of.Junctions", "Junctions.density", "Total.Vessels.Length", "Average.Vessels.Length", "Total.Number.of.End.Points", "Average.Lacunarity"), row.names = c(NA, -5L), class = "data.frame")


doBarPlot <- function(x) {
  p <- ggplot(x, aes_string(x="Type", y=colnames(x), fill="Type") ) +
    stat_summary(fun.y = "mean", geom = "bar", na.rm = TRUE) +
    stat_summary(fun.data = "mean_cl_normal", geom = "errorbar", width=0.5, na.rm = TRUE) +
    ggtitle("VMO vs. VMT") +
    theme(plot.title = element_text(hjust = 0.5) )
  print(p)
  ggsave(sprintf("plots/%s_bars.pdf", colnames(x) ) )
  return(p)
}

c = c('Total.Vessels.Length', 'Total.Number.of.Junctions', 'Total.Number.of.End.Points', 'Average.Lacunarity')
p[c] <- lapply(df[c], doBarPlot)

但是这会产生以下错误:

Error: ggplot2 doesn't know how to deal with data of class numeric

调试显示 doBarPlot 内部的xnumeric 类型而不是data.frame,因此出现ggplot 错误。但是,test &lt;- df2[c] 会生成 data.frame 类型的变量。

为什么xnumeric? 在不使用循环的情况下应用 doBarPlot 的最佳方式是什么?

【问题讨论】:

  • lapply 将每一列作为向量拉出(请参阅lapply(iris, class)),而 ggplot 需要一个 data.frame。简单的解决方案是gather数据然后使用facet_wrap
  • 您也可以考虑仅将要绘制的列的名称传递给函数,而不是数据框。此外,在提出问题时,提供reproducible example 会让其他人更容易为您提供帮助。
  • @MikkoMarttila 我添加了一个示例数据框。希望这会有所帮助

标签: r ggplot2 lapply


【解决方案1】:

正如其他人所指出的,您最初方法的问题在于,当您在数据框上使用 lapply 时,您要迭代的元素将是列向量,而不是 1 列数据框。但是,即使您确实迭代了 1 列数据框,您的函数也会失败:提供给 ggplot 调用的数据框不会包含您在图中使用的 Type 列。

相反,您可以修改函数以采用两个参数:完整的数据框,以及要在 y 轴上使用的列的名称。

doBarPlot <- function(data, y) {
  p <- ggplot(data, aes_string(x = "Type", y = y, fill = "Type")) +
    stat_summary(fun.y = "mean", geom = "bar", na.rm = TRUE) +
    stat_summary(
      fun.data = "mean_cl_normal",
      geom = "errorbar",
      width = 0.5,
      na.rm = TRUE
    ) +
    ggtitle("VMO vs. VMT") +
    theme(plot.title = element_text(hjust = 0.5))
  print(p)
  ggsave(sprintf("plots/%s_bars.pdf", y))
  return(p)
}

然后,您可以使用lapply 来迭代要绘制的列的字符向量,同时通过... 提供数据框作为绘图函数的固定参数:

library(ggplot2)

cols <- c('Total.Vessels.Length', 'Total.Number.of.Junctions',
          'Total.Number.of.End.Points', 'Average.Lacunarity')
p <- lapply(cols, doBarPlot, data = df)

此外,如果您不介意将所有绘图放在一个文件中,您还可以使用 tidyr::gather 将数据重新整形为长格式,并在绘图中使用 facet_wrap(如 @RichardTelford 在他的评论),完全避免了迭代和对函数的需求:

library(tidyverse)

df %>% 
  gather(variable, value, cols) %>% 
  ggplot(aes(x = Type, y = value, fill = Type)) +
    facet_wrap(~ variable, scales = "free_y") +
    stat_summary(fun.y = "mean", geom = "bar", na.rm = TRUE) +
    stat_summary(
      fun.data = "mean_cl_normal",
      geom = "errorbar",
      width = 0.5,
      na.rm = TRUE
    ) +
    ggtitle("VMO vs. VMT") +
    theme(plot.title = element_text(hjust = 0.5))

【讨论】:

  • 非常感谢...我尝试了类似于p &lt;- lapply(cols, doBarPlot, data = df) 的方法,但显然错误地传递了第二个输入变量,所以它出错了。很有帮助
【解决方案2】:

apply 系列函数矢量化传递的对象。一个简单的例子来说明这一点:

lapply(mtcars, function(x) print(x))

使用您的代码,您将df 中每一列的向量传递给函数doBarPlotggplot2 包适用于数据帧,而不是列表或向量,因此您会收到错误消息。

如果你想使用你的函数,直接应用到子集df

doBarPlot(df[ , c])

如果您有一堆数据框,并且您想按c 中的列进行子集化,请查看此答案: How to apply same function to every specified column in a data.table

或者,查看dplyr::select()

【讨论】:

  • doBarPlot(df[c]) 似乎不起作用,因为它没有为c 中的每一列创建一个图......只是第一个。
  • 我误解了你的问题。我会尽快更新我的答案。
猜你喜欢
  • 1970-01-01
  • 2017-05-26
  • 2018-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-21
  • 2020-01-02
  • 2021-07-01
相关资源
最近更新 更多