根据几列生成一系列新行
Generate a sequence of new rows based on a few columns
我想为数据框中的一组变量按顺序创建新行。例如,我有这些虚拟数据
data1 <- data.frame(id = c('JUJ', 'SJD'),
sex = c('male', 'female'),
year = c(2000, 2010),
age = c(48, 75), blood = c(6.85, 4.6))
data1
| id | sex | year | age | blood |
|-----|--------|------|-----|-------|
| JUJ | male | 2000 | 48 | 6.85 |
| SJD | female | 2010 | 75 | 4.6 |
我想为每个 id
再生成 4 个观察结果(作为行)。对于 year
和 age
,每个新行都应比上一行大 1 个单位。对于某些变量,例如在这些数据中,sex
和 blood
应该在所有行中保持相同。
我确信 R 中的 seq()
函数可以工作,但我如何才能找到正确的使用方法。如果解决方案包含 tidyverse
函数,我会更喜欢。
最后,数据看起来像这样
data2 <- data.frame(id = c('JUJ', 'JUJ', 'JUJ', 'JUJ', 'SJD', 'SJD',
'SJD', 'SJD'),
sex = c('male', 'male', 'male', 'male', 'female',
'female', 'female', 'female'),
year = c(2000, 2001, 2002, 2003, 2010, 2011, 2012, 2013),
age = c(48, 49, 50, 51, 75, 76, 77, 78),
blood = c(6.85, 6.85, 6.85, 6.85, 4.6, 4.6, 4.6, 4.6))
data2
| id | sex | year | age | blood |
|-----|--------|------|-----|-------|
| JUJ | male | 2000 | 48 | 6.85 |
| JUJ | male | 2001 | 49 | 6.85 |
| JUJ | male | 2002 | 50 | 6.85 |
| JUJ | male | 2003 | 51 | 6.85 |
| SJD | female | 2010 | 75 | 4.6 |
| SJD | female | 2011 | 76 | 4.6 |
| SJD | female | 2012 | 77 | 4.6 |
| SJD | female | 2013 | 78 | 4.6 |
我们可以使用 slice
重复行 n
次,group_by
id 并依次递增 age
和 year
列。
library(dplyr)
n <- 4
data1 %>%
slice(rep(seq_len(n()), each = n)) %>%
group_by(id) %>%
mutate_at(vars(year, age), ~. + 0:(n - 1))
# id sex year age blood
# <fct> <fct> <dbl> <dbl> <dbl>
#1 JUJ male 2000 48 6.85
#2 JUJ male 2001 49 6.85
#3 JUJ male 2002 50 6.85
#4 JUJ male 2003 51 6.85
#5 SJD female 2010 75 4.6
#6 SJD female 2011 76 4.6
#7 SJD female 2012 77 4.6
#8 SJD female 2013 78 4.6
另一种 dplyr
和 tidyr
的可能性是:
data1 %>%
group_by(id) %>%
uncount(4) %>%
mutate_at(vars(year, age), ~ . + row_number() - 1)
id sex year age blood
<fct> <fct> <dbl> <dbl> <dbl>
1 JUJ male 2000 48 6.85
2 JUJ male 2001 49 6.85
3 JUJ male 2002 50 6.85
4 JUJ male 2003 51 6.85
5 SJD female 2010 75 4.6
6 SJD female 2011 76 4.6
7 SJD female 2012 77 4.6
8 SJD female 2013 78 4.6
另一个tidyverse
解决方案:
library(tidyverse)
data1 %>%
mutate_at(vars(year, age), list(~ map(. ,~seq(.x, .x + 4 - 1))))%>%
unnest %>% select(-blood, blood)
#> id sex year age blood
#> 1 JUJ male 2000 48 6.85
#> 2 JUJ male 2001 49 6.85
#> 3 JUJ male 2002 50 6.85
#> 4 JUJ male 2003 51 6.85
#> 5 SJD female 2010 75 4.60
#> 6 SJD female 2011 76 4.60
#> 7 SJD female 2012 77 4.60
#> 8 SJD female 2013 78 4.60
我想为数据框中的一组变量按顺序创建新行。例如,我有这些虚拟数据
data1 <- data.frame(id = c('JUJ', 'SJD'),
sex = c('male', 'female'),
year = c(2000, 2010),
age = c(48, 75), blood = c(6.85, 4.6))
data1
| id | sex | year | age | blood |
|-----|--------|------|-----|-------|
| JUJ | male | 2000 | 48 | 6.85 |
| SJD | female | 2010 | 75 | 4.6 |
我想为每个 id
再生成 4 个观察结果(作为行)。对于 year
和 age
,每个新行都应比上一行大 1 个单位。对于某些变量,例如在这些数据中,sex
和 blood
应该在所有行中保持相同。
我确信 R 中的 seq()
函数可以工作,但我如何才能找到正确的使用方法。如果解决方案包含 tidyverse
函数,我会更喜欢。
最后,数据看起来像这样
data2 <- data.frame(id = c('JUJ', 'JUJ', 'JUJ', 'JUJ', 'SJD', 'SJD',
'SJD', 'SJD'),
sex = c('male', 'male', 'male', 'male', 'female',
'female', 'female', 'female'),
year = c(2000, 2001, 2002, 2003, 2010, 2011, 2012, 2013),
age = c(48, 49, 50, 51, 75, 76, 77, 78),
blood = c(6.85, 6.85, 6.85, 6.85, 4.6, 4.6, 4.6, 4.6))
data2
| id | sex | year | age | blood |
|-----|--------|------|-----|-------|
| JUJ | male | 2000 | 48 | 6.85 |
| JUJ | male | 2001 | 49 | 6.85 |
| JUJ | male | 2002 | 50 | 6.85 |
| JUJ | male | 2003 | 51 | 6.85 |
| SJD | female | 2010 | 75 | 4.6 |
| SJD | female | 2011 | 76 | 4.6 |
| SJD | female | 2012 | 77 | 4.6 |
| SJD | female | 2013 | 78 | 4.6 |
我们可以使用 slice
重复行 n
次,group_by
id 并依次递增 age
和 year
列。
library(dplyr)
n <- 4
data1 %>%
slice(rep(seq_len(n()), each = n)) %>%
group_by(id) %>%
mutate_at(vars(year, age), ~. + 0:(n - 1))
# id sex year age blood
# <fct> <fct> <dbl> <dbl> <dbl>
#1 JUJ male 2000 48 6.85
#2 JUJ male 2001 49 6.85
#3 JUJ male 2002 50 6.85
#4 JUJ male 2003 51 6.85
#5 SJD female 2010 75 4.6
#6 SJD female 2011 76 4.6
#7 SJD female 2012 77 4.6
#8 SJD female 2013 78 4.6
另一种 dplyr
和 tidyr
的可能性是:
data1 %>%
group_by(id) %>%
uncount(4) %>%
mutate_at(vars(year, age), ~ . + row_number() - 1)
id sex year age blood
<fct> <fct> <dbl> <dbl> <dbl>
1 JUJ male 2000 48 6.85
2 JUJ male 2001 49 6.85
3 JUJ male 2002 50 6.85
4 JUJ male 2003 51 6.85
5 SJD female 2010 75 4.6
6 SJD female 2011 76 4.6
7 SJD female 2012 77 4.6
8 SJD female 2013 78 4.6
另一个tidyverse
解决方案:
library(tidyverse)
data1 %>%
mutate_at(vars(year, age), list(~ map(. ,~seq(.x, .x + 4 - 1))))%>%
unnest %>% select(-blood, blood)
#> id sex year age blood
#> 1 JUJ male 2000 48 6.85
#> 2 JUJ male 2001 49 6.85
#> 3 JUJ male 2002 50 6.85
#> 4 JUJ male 2003 51 6.85
#> 5 SJD female 2010 75 4.60
#> 6 SJD female 2011 76 4.60
#> 7 SJD female 2012 77 4.60
#> 8 SJD female 2013 78 4.60