[英]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.