R语言 & Stata:如何对字符串变量实现分组求和和分组累加?

最近有个培训班的小伙伴遇到了这样的问题,他想对字符串变量进行分组求和与分组累加,但是他不知道该如何实现,今天我们就来一起看一下。

Stata 的实现

首先我们先生成一份示例数据:

* 生成示例数据
clear
input int group int x str1 y
1 12 "A"
1 2 "B"
1 35 "C"
1 56 "D"
1 34 "E"
1 24 "F"
1 87 "G"
2 1 "a"
2 36 "b"
2 78 "c"
2 34 "d"
2 12 "e"
2 3 "f"
2 0 "g"
end
list, sep(7)

*>     +----------------+
*>     | group    x   y |
*>     |----------------|
*>  1. |     1   12   A |
*>  2. |     1    2   B |
*>  3. |     1   35   C |
*>  4. |     1   56   D |
*>  5. |     1   34   E |
*>  6. |     1   24   F |
*>  7. |     1   87   G |
*>     |----------------|
*>  8. |     2    1   a |
*>  9. |     2   36   b |
*> 10. |     2   78   c |
*> 11. |     2   34   d |
*> 12. |     2   12   e |
*> 13. |     2    3   f |
*> 14. |     2    0   g |
*>     +----------------+

其中 group 变量是分组变量,x 是数值型变量,y 是字符型变量。

对于数值型变量,egen 的 sum() 函数可以实现分组求和的功能:

bysort group: egen xsum = sum(x)
list, sep(7)

*>     +-----------------------+
*>     | group    x   y   xsum |
*>     |-----------------------|
*>  1. |     1   12   A    250 |
*>  2. |     1    2   B    250 |
*>  3. |     1   35   C    250 |
*>  4. |     1   56   D    250 |
*>  5. |     1   34   E    250 |
*>  6. |     1   24   F    250 |
*>  7. |     1   87   G    250 |
*>     |-----------------------|
*>  8. |     2    1   a    164 |
*>  9. |     2   36   b    164 |
*> 10. |     2   78   c    164 |
*> 11. |     2   34   d    164 |
*> 12. |     2   12   e    164 |
*> 13. |     2    3   f    164 |
*> 14. |     2    0   g    164 |
*>     +-----------------------+

gen 的 sum() 函数可以实现分组累加的功能:

bysort group: gen xcumsum = sum(x)
list, sep(7)

*>     +---------------------------------+
*>     | group    x   y   xsum   xcumsum |
*>     |---------------------------------|
*>  1. |     1   12   A    250        12 |
*>  2. |     1    2   B    250        14 |
*>  3. |     1   35   C    250        49 |
*>  4. |     1   56   D    250       105 |
*>  5. |     1   34   E    250       139 |
*>  6. |     1   24   F    250       163 |
*>  7. |     1   87   G    250       250 |
*>     |---------------------------------|
*>  8. |     2    1   a    164         1 |
*>  9. |     2   36   b    164        37 |
*> 10. |     2   78   c    164       115 |
*> 11. |     2   34   d    164       149 |
*> 12. |     2   12   e    164       161 |
*> 13. |     2    3   f    164       164 |
*> 14. |     2    0   g    164       164 |
*>     +---------------------------------+

但是上面两个都不能应用与字符串变量,但是字符串变量的加法在 Stata 中是可行的,例如:

gen yy = y + y
list, sep(7)

*>     +--------------------------------------+
*>     | group    x   y   xsum   xcumsum   yy |
*>     |--------------------------------------|
*>  1. |     1   12   A    250        12   AA |
*>  2. |     1    2   B    250        14   BB |
*>  3. |     1   35   C    250        49   CC |
*>  4. |     1   56   D    250       105   DD |
*>  5. |     1   34   E    250       139   EE |
*>  6. |     1   24   F    250       163   FF |
*>  7. |     1   87   G    250       250   GG |
*>     |--------------------------------------|
*>  8. |     2    1   a    164         1   aa |
*>  9. |     2   36   b    164        37   bb |
*> 10. |     2   78   c    164       115   cc |
*> 11. |     2   34   d    164       149   dd |
*> 12. |     2   12   e    164       161   ee |
*> 13. |     2    3   f    164       164   ff |
*> 14. |     2    0   g    164       164   gg |
*>     +--------------------------------------+

那我们如何在字符串变量上实现分组求和以及分组累加的功能呢?

可以通过设计下面的命令实现:

  1. 实现 egen 的 sum() 功能,也就是分组求和:
gen ysum = ""
levelsof group, local(group)
foreach i in `group' {
 di "`i'"
 local `i' = ""
 forval j = 1/`=_N'{
  if `=group[`j']' == `i'{
   local `i' = "``i''`=y[`j']'"
  }
 }
 replace ysum = "``i''" if group == `i'
}
list, sep(7)

*>     +------------------------------------------------+
*>     | group    x   y   xsum   xcumsum   yy      ysum |
*>     |------------------------------------------------|
*>  1. |     1   12   A    250        12   AA   ABCDEFG |
*>  2. |     1    2   B    250        14   BB   ABCDEFG |
*>  3. |     1   35   C    250        49   CC   ABCDEFG |
*>  4. |     1   56   D    250       105   DD   ABCDEFG |
*>  5. |     1   34   E    250       139   EE   ABCDEFG |
*>  6. |     1   24   F    250       163   FF   ABCDEFG |
*>  7. |     1   87   G    250       250   GG   ABCDEFG |
*>     |------------------------------------------------|
*>  8. |     2    1   a    164         1   aa   abcdefg |
*>  9. |     2   36   b    164        37   bb   abcdefg |
*> 10. |     2   78   c    164       115   cc   abcdefg |
*> 11. |     2   34   d    164       149   dd   abcdefg |
*> 12. |     2   12   e    164       161   ee   abcdefg |
*> 13. |     2    3   f    164       164   ff   abcdefg |
*> 14. |     2    0   g    164       164   gg   abcdefg |
*>     +------------------------------------------------+
  1. 实现 gen 的 sum() 功能,也就是分组累加:
gen ycumsum = ""
levelsof group, local(group)
foreach i in `group' {
 local `i' = ""
 forval j = 1/`=_N'{
  if `=group[`j']' == `i'{
   local `i' = "``i''`=y[`j']'"
  }
  replace ycumsum = "``i''" if group == `i' & _n == `j'
 }
}
list, sep(7)

*>     +----------------------------------------------------------+
*>     | group    x   y   xsum   xcumsum   yy      ysum   ycumsum |
*>     |----------------------------------------------------------|
*>  1. |     1   12   A    250        12   AA   ABCDEFG         A |
*>  2. |     1    2   B    250        14   BB   ABCDEFG        AB |
*>  3. |     1   35   C    250        49   CC   ABCDEFG       ABC |
*>  4. |     1   56   D    250       105   DD   ABCDEFG      ABCD |
*>  5. |     1   34   E    250       139   EE   ABCDEFG     ABCDE |
*>  6. |     1   24   F    250       163   FF   ABCDEFG    ABCDEF |
*>  7. |     1   87   G    250       250   GG   ABCDEFG   ABCDEFG |
*>     |----------------------------------------------------------|
*>  8. |     2    1   a    164         1   aa   abcdefg         a |
*>  9. |     2   36   b    164        37   bb   abcdefg        ab |
*> 10. |     2   78   c    164       115   cc   abcdefg       abc |
*> 11. |     2   34   d    164       149   dd   abcdefg      abcd |
*> 12. |     2   12   e    164       161   ee   abcdefg     abcde |
*> 13. |     2    3   f    164       164   ff   abcdefg    abcdef |
*> 14. |     2    0   g    164       164   gg   abcdefg   abcdefg |
*>     +----------------------------------------------------------+

这样就完成了!

R 语言的实现

使用 R 语言完成这个工作更容易些,首先我们读取上面生成的示例数据:

library(tidyverse)
haven::read_dta('strsum.dta') -> df
df

#> # A tibble: 14 x 3
#>    group     x y    
#>    <dbl> <dbl> <chr>
#>  1     1    12 A    
#>  2     1     2 B    
#>  3     1    35 C    
#>  4     1    56 D    
#>  5     1    34 E    
#>  6     1    24 F    
#>  7     1    87 G    
#>  8     2     1 a    
#>  9     2    36 b    
#> 10     2    78 c    
#> 11     2    34 d    
#> 12     2    12 e    
#> 13     2     3 f    
#> 14     2     0 g 

x 变量是数值型变量,分组求和或累加都很容易:

df %>% 
  group_by(group) %>% 
  mutate(xsum = sum(x)) %>% 
  mutate(xcumsum = cumsum(x)) %>% 
  ungroup()

#> # A tibble: 14 x 5
#>    group     x y      xsum xcumsum
#>    <dbl> <dbl> <chr> <dbl>   <dbl>
#>  1     1    12 A       250      12
#>  2     1     2 B       250      14
#>  3     1    35 C       250      49
#>  4     1    56 D       250     105
#>  5     1    34 E       250     139
#>  6     1    24 F       250     163
#>  7     1    87 G       250     250
#>  8     2     1 a       164       1
#>  9     2    36 b       164      37
#> 10     2    78 c       164     115
#> 11     2    34 d       164     149
#> 12     2    12 e       164     161
#> 13     2     3 f       164     164
#> 14     2     0 g       164     164

那么对于字符串变量 y 该如何分组求和(连接)或者累加(逐次连接)呢?

根据我的经验,分组求和很简单:

df %>% 
  group_by(group) %>% 
  mutate(ysum = paste0(y, collapse = ""))

#> # A tibble: 14 x 4
#> # Groups:   group [2]
#>    group     x y     ysum   
#>    <dbl> <dbl> <chr> <chr>  
#>  1     1    12 A     ABCDEFG
#>  2     1     2 B     ABCDEFG
#>  3     1    35 C     ABCDEFG
#>  4     1    56 D     ABCDEFG
#>  5     1    34 E     ABCDEFG
#>  6     1    24 F     ABCDEFG
#>  7     1    87 G     ABCDEFG
#>  8     2     1 a     abcdefg
#>  9     2    36 b     abcdefg
#> 10     2    78 c     abcdefg
#> 11     2    34 d     abcdefg
#> 12     2    12 e     abcdefg
#> 13     2     3 f     abcdefg
#> 14     2     0 g     abcdefg

分组累加我们先找一个向量试试:

letters
#>  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r"
#> [19] "s" "t" "u" "v" "w" "x" "y" "z"

letters2 <- c()
for (i in 1:length(letters)) {
  letters2[i] <- paste0(letters[1:i], collapse = "")
}
letters2
#>  [1] "a"                          "ab"                        
#>  [3] "abc"                        "abcd"                      
#>  [5] "abcde"                      "abcdef"                    
#>  [7] "abcdefg"                    "abcdefgh"                  
#>  [9] "abcdefghi"                  "abcdefghij"                
#> [11] "abcdefghijk"                "abcdefghijkl"              
#> [13] "abcdefghijklm"              "abcdefghijklmn"            
#> [15] "abcdefghijklmno"            "abcdefghijklmnop"          
#> [17] "abcdefghijklmnopq"          "abcdefghijklmnopqr"        
#> [19] "abcdefghijklmnopqrs"        "abcdefghijklmnopqrst"      
#> [21] "abcdefghijklmnopqrstu"      "abcdefghijklmnopqrstuv"    
#> [23] "abcdefghijklmnopqrstuvw"    "abcdefghijklmnopqrstuvwx"  
#> [25] "abcdefghijklmnopqrstuvwxy"  "abcdefghijklmnopqrstuvwxyz"

这样我们就可以自己编写一个函数:

strcumsum <- function(x){
  c <- c()
  for (i in 1:length(x)) {
    c[i] <- paste0(x[1:i], collapse = "")
  }
  return(c)
}
strcumsum(letters)

然后就可以用这个函数对字符串变量进行分组累加了:

df %>% 
  group_by(group) %>% 
  mutate(ysum = paste0(y, collapse = "")) %>% 
  mutate(ycumsum = strcumsum(y))

#> # A tibble: 14 x 5
#> # Groups:   group [2]
#>    group     x y     ysum    ycumsum
#>    <dbl> <dbl> <chr> <chr>   <chr>  
#>  1     1    12 A     ABCDEFG A      
#>  2     1     2 B     ABCDEFG AB     
#>  3     1    35 C     ABCDEFG ABC    
#>  4     1    56 D     ABCDEFG ABCD   
#>  5     1    34 E     ABCDEFG ABCDE  
#>  6     1    24 F     ABCDEFG ABCDEF 
#>  7     1    87 G     ABCDEFG ABCDEFG
#>  8     2     1 a     abcdefg a      
#>  9     2    36 b     abcdefg ab     
#> 10     2    78 c     abcdefg abc    
#> 11     2    34 d     abcdefg abcd   
#> 12     2    12 e     abcdefg abcde  
#> 13     2     3 f     abcdefg abcdef 
#> 14     2     0 g     abcdefg abcdefg
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容