汇总多组列

时间:2018-05-22 05:17:41

标签: r group-by dplyr purrr summarization

我的情况是我的数据框包含图像分析的结果,其中列是图像中存在的特定类的比例,因此示例数据框class_df将如下所示:

id    A    B    C    D    E    F
 1 0.20 0.30 0.10 0.15 0.25 0.00 
 2 0.05 0.10 0.05 0.30 0.10 0.40
 3 0.10 0.10 0.10 0.20 0.20 0.30

这些类中的每一个都属于一个功能组,我想创建新的列,其中每个功能组的比例是从类中计算出来的。映射class_fg

的示例
class         fg
    A          Z
    B          Z
    C          Z
    D          Y
    E          Y
    F          X

并且所需的结果是(添加行以显示所需的新列):

id    A    B    C    D    E    F |    X    Y    Z
 1 0.20 0.30 0.10 0.15 0.25 0.00 | 0.00 0.40 0.60
 2 0.05 0.10 0.05 0.30 0.10 0.40 | 0.40 0.40 0.20
 3 0.10 0.10 0.10 0.20 0.20 0.30 | 0.30 0.40 0.30

我可以使用

一次完成一个功能组
first_fg <- class_fg %>%
  filter(fg == "Z") %>%
  select(class) %>%
  unlist()

class_df <- class_df %>%
  mutate(Z = rowSums(select(., one_of(first_fg))))

当然有更好的方法可以计算每个功能组的行总和,而不必为每个组重复此代码?也许使用purrr?

5 个答案:

答案 0 :(得分:7)

我们可以split&#39; class_df&#39;通过&#39; class&#39;,使用list循环map元素,select&#39; class_df&#39;并获得rowSums

library(tidyverse)
class_fg %>%
    split(.$fg) %>% 
    map_df(~ class_df %>%
                select(one_of(.x$class)) %>% 
                rowSums) %>%
    bind_cols(class_df, .)
#  id    A   B    C    D    E   F   X   Y   Z
#1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
#2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
#3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3

或按nest进行分组,然后rowSumsmap ping list元素

class_fg %>% 
   group_by(fg) %>%
   nest %>%
   mutate(out = map(data, ~  class_df %>%
                               select(one_of(.x$class)) %>% 
                               rowSums)) %>% 
   select(-data)  %>%
   unnest %>% 
   unstack(., out ~ fg) %>% 
   bind_cols(class_df, .)

答案 1 :(得分:6)

始终以长格式处理数据更容易。因此,使用class_dftidyr:gather更改为长格式,然后加入class_fg。以长格式对数据执行分析。最后,以宽格式传播以匹配预期结果。

library(tidyverse)

class_df %>% gather(key, value, -id) %>% 
  inner_join(class_fg, by=c("key" = "class")) %>%
  group_by(id, fg) %>%
  summarise(value = sum(value)) %>%
  spread(fg, value) %>%
  inner_join(class_df, by="id") %>% as.data.frame()

#   id   X   Y   Z    A   B    C    D    E   F
# 1  1 0.0 0.4 0.6 0.20 0.3 0.10 0.15 0.25 0.0
# 2  2 0.4 0.4 0.2 0.05 0.1 0.05 0.30 0.10 0.4
# 3  3 0.3 0.4 0.3 0.10 0.1 0.10 0.20 0.20 0.3

数据:

class_fg <- read.table(text = 
"class         fg
                 A          Z
                 B          Z
                 C          Z
                 D          Y
                 E          Y
                 F          X",
header = TRUE, stringsAsFactors = FALSE)

class_df  <- read.table(text = 
"id    A    B    C    D    E    F
1 0.20 0.30 0.10 0.15 0.25 0.00 
2 0.05 0.10 0.05 0.30 0.10 0.40
3 0.10 0.10 0.10 0.20 0.20 0.30",
header = TRUE, stringsAsFactors = FALSE)

答案 2 :(得分:5)

又一种选择,以及已经提供的工作解决方案, 将使用https://github.com/coobird/thumbnailator 使用library(tidyverse) 包来构建表达式来计算每个表达式中的总和 基。

psum <- function(...) reduce(list(...), `+`)

首先,定义一个辅助函数,用于执行向量的元素和:

class_fg

将分组从sum_exprs <- with(class_fg, split(class, fg)) %>% map(~ rlang::expr(psum(!!!rlang::syms(.x)))) sum_exprs #> $X #> psum(F) #> #> $Y #> psum(D, E) #> #> $Z #> psum(A, B, C) 提取到列表中然后我们可以构建 用于计算每组总和的表达式列表:

mutate

准备好表达式列表后,我们可以quasiquotation将它们class_df %>% mutate(!!!sum_exprs) #> id A B C D E F X Y Z #> 1 1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6 #> 2 2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2 #> 3 3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3 加入到#disable multi-touch emulation from kivy.config import Config Config.set('input', 'mouse', 'mouse,multitouch_on_demand') from kivy.app import App from kivy.lang import Builder from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.image import Image #-------------------------------------------------------------------- Builder.load_string(""" <RootWidget>: BoxLayout: orientation: 'vertical' padding: "10dp" BoxLayout: size_hint_y: 3 Widget: # to fill the line from left Button: text: "Button" font_size: 40 text_size: self.size halign: 'center' valign: 'bottom' padding_y: 10 #Adding Image you can comment this part Image: source: 'img/calender1.png' center_x: self.parent.center_x center_y: self.parent.center_y +10 Widget:# to fill the line from right BoxLayout: size_hint_y: 10 """) #----------------------------------------------------------------------- class RootWidget(BoxLayout): pass #----------------------------------------------------------------------- class MyApp(App): def build(self): return RootWidget() if __name__ == '__main__': MyApp().run() 的数据中:

Button:
    text: "Button"
    font_size: 40
    text_size: self.size     
    halign: 'center'
    valign: 'bottom'
    padding_y: 10

(我在他的答案中使用了@MKR提供的代码来创建数据)。

"bang-bang-bang" (!!!)(v0.2.0)创建于2018-05-22。

答案 3 :(得分:1)

我通常的方法是坚持base R,只要数据集不会太大。在您的情况下,base R解决方案将是:

class_df=as.data.frame(
  c(class_df,
    lapply(split(class_fg,class_fg$fg),
           function(x) rowSums(class_df[,x$class,drop=FALSE]))))
class_df
#  id    A   B    C    D    E   F   X   Y   Z
#1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
#2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
#3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3

如果数据集太大,我使用data.table。解决问题的data.table解决方案:

library(data.table)

class_dt=data.table(class_df)
grps=split(class_fg,class_fg$fg)

for (g in grps) class_dt[,c(g$fg[1]):=rowSums(.SD),.SDcols=g$class,]
class_dt
#   id    A   B    C    D    E   F   X   Y   Z
#1:  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
#2:  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
#3:  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3

答案 4 :(得分:0)

在列子集上使用tidyverse的另一个rowSums解决方案:

library(tidyverse)
class_fg %>%
  group_by(fg) %>% 
  summarize(list(rowSums(class_df[class]))) %>%
  spread(1,2) %>%
  unnest() %>%
  bind_cols(class_df, .)

#>   id    A   B    C    D    E   F   X   Y   Z
#> 1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
#> 2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
#> 3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3

或者为了荣耀代码高尔夫:

x <- with(class_fg, tapply(class, fg, reformulate))
mutate(class_df, !!!map(x, ~as.list(.)[[2]]))
#>   id    A   B    C    D    E   F   X   Y   Z
#> 1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
#> 2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
#> 3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3