繁体   English   中英

将组标识符分配给落在包含 R 中字符串的行之间的行组

[英]Assign group identifiers to groups of rows falling between rows containing a string in R

我得到了一个 Excel 文件,其中每组数据的末尾都标有一行空白,除了一个单元格包含一个字符串,如“Person 1”、“Person 2”、“Person 3”和很快。 属于人 1 的数据位于包含“人 1”的行之前的行中,属于人 2 的数据位于包含“人 1”的行和包含“人 2”的行之间的行中。 遵循此模式直到文件末尾,其中最后一行包含带有“Person 100”的单元格。 更有趣的是,“Person [n]”字符串并不总是在同一列中,每个人的行数可能不同。 请参阅下面的玩具示例。

 df_1 <- data.frame(iv1=c(rbinom(3,1,.4), NA, rbinom(4,1,.4), NA, rbinom(2,1,.4), NA),
            iv2=c(rbinom(3,1,.4), NA, rbinom(4,1,.4), NA, rbinom(2,1,.4), NA),
            iv3=c(rbinom(3,1,.4), "Person 1", rbinom(4,1,.4), NA, rbinom(2,1,.4), "Person 3"),
            dv1=c(rbinom(3,1,.4), NA, rbinom(4,1,.4), "Person 2", rbinom(2,1,.4), NA),
            dv2=c(rbinom(3,1,.4), NA, rbinom(4,1,.4), NA, rbinom(2,1,.4), NA),
            dv3=c(rbinom(3,1,.4), NA, rbinom(4,1,.4), NA, rbinom(2,1,.4), NA))

产生这个数据框

   iv1 iv2      iv3      dv1 dv2 dv3
1    1   1        0        1   1   0
2    0   0        1        0   0   0
3    1   0        0        1   0   1
4   NA  NA Person 1     <NA>  NA  NA
5    1   1        0        0   1   1
6    1   0        0        0   0   0
7    0   0        0        1   0   0
8    1   0        0        1   1   1
9   NA  NA     <NA> Person 2  NA  NA
10   0   0        0        1   0   0
11   0   1        0        0   0   1
12  NA  NA Person 3     <NA>  NA  NA

我想做的是创建一个新列(“Person_ID”)来标识属于每个人的数据,因此对于属于人 1 的行,Person_ID 等于 1,对于属于人 2 的行,Person_ID 等于 2,依此类推打开,如下面的数据框所示。

  iv1 iv2 iv3 dv1 dv2 dv3 Person_ID
1   1   1   0   1   1   0         1
2   0   0   1   0   0   0         1
3   1   0   0   1   0   1         1
4   1   1   0   0   1   1         2
5   1   0   0   0   0   0         2
6   0   0   0   1   0   0         2
7   1   0   0   1   1   1         2
8   0   0   0   1   0   0         3
9   0   1   0   0   0   1         3

我喜欢基于 dplyr 的解决方案,但当然,我对任何可行的方法都持开放态度。 谢谢!

我们可以这样做: iv1:dv3中的值不匹配,因为您没有设置种子:

第一个解决方案取决于NA可能会干扰其他NA数据。 第二个解决方案与NA无关:

library(dplyr)

df_1 %>% 
  mutate(Person_ID=cumsum(is.na(iv1))+1) %>% 
  na.omit()
   iv1   iv2 iv3   dv1     dv2   dv3 Person_ID
  <int> <int> <chr> <chr> <int> <int>     <dbl>
1     0     0 0     0         0     0         1
2     1     1 1     0         1     0         1
3     1     0 0     0         0     0         1
4     1     1 0     0         0     1         2
5     1     1 0     0         0     1         2
6     1     0 0     0         1     1         2
7     0     0 1     1         1     0         2
8     0     0 1     0         0     0         3
9     1     1 0     1         0     0         3

另一种方式可能是:

library(tidyverse)

df_1 %>% 
  mutate(Person_ID = coalesce(iv3, dv1),
         Person_ID = ifelse(str_detect(Person_ID, "Person"), parse_number(Person_ID), NA)) %>% 
  fill(Person_ID, .direction = "up") %>% 
  na.omit()

这是另一种选择:

library(tidyverse)

df_1 %>% 
  unite(Person_ID, everything(), sep = ",", remove = FALSE) %>% 
  mutate(Person_ID = str_extract(Person_ID, "(?<=Person )[0-9]*")) %>% 
  fill(Person_ID, .direction = "up") %>% 
  slice(-which(rowSums(t(apply(df_1, 1, grepl, pattern="Person"))) == 1))

或者另一种选择可能是:

df_1 %>% 
  mutate(across(everything(), ~str_extract(., "(?<=Person )[0-9]*")),
         Person_ID = coalesce(iv3, dv1)) %>% 
  fill(Person_ID, .direction = "up") %>% 
  select(Person_ID) %>% 
  bind_cols(., df_1) %>% 
  na.omit()

Output

  Person_ID iv1 iv2 iv3 dv1 dv2 dv3
1         1   0   1   1   1   0   0
2         1   0   0   1   1   0   0
3         1   0   1   1   1   1   0
4         2   1   1   1   0   0   0
5         2   0   0   0   0   1   1
6         2   1   1   0   1   0   0
7         2   1   0   1   0   1   1
8         3   1   1   1   1   0   0
9         3   0   0   1   0   0   1

暂无
暂无

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

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