根据顺序行值创建新数据框
Create new dataframe based on sequential row values
我有一个名字和年份的数据框,有一个虚拟变量来表示名字是否出现在一年中。
我正在尝试创建一个数据框来告诉我
-
- 当年出现的名字总数,
-
- 当年出现而前年未出现的数量
在下面的例子中,2017 年只有一个人(Terry)出现,而前一年没有出现,因此 total 和 new 都是 1。2018 年出现了三个人,但只有两个是新人,因为 Terry 出现了在前一年。如果有人在2017年和2019年出现,但在2018年没有出现,那么他们应该被归类为2019年的新人。
例子
Name x2017 x2018 x2019
1 Terry 1 1 0
2 Sam 0 0 1
3 Nic 0 1 1
4 Sarah 0 1 1
代码
data.frame(
Name = c("Terry", "Sam", "Nic", "Sarah"),
x2017 = c(1, 0, 0, 0),
x2018 = c(1, 0, 1, 1),
x2019 = c(0, 1, 1, 1)
)
我正在尝试创建的输出
Year Total New
1 2017 1 1
2 2018 3 2
3 2019 3 1
我试过过滤和使用行总和,但我觉得有一个我不知道的函数可以做到这一点。
谢谢!
也许这就是您要找的:
- 重塑为长格式,例如使用
tidy::pivot_longer
- 分组
Name
并利用 dplyr::lag
添加一个人是否是新人的指标
- 按年份汇总
d <- data.frame(
Name = c("Terry", "Sam", "Nic", "Sarah"),
x2017 = c(1, 0, 0, 0),
x2018 = c(1, 0, 1, 1),
x2019 = c(0, 1, 1, 1)
)
library(dplyr)
library(tidyr)
d %>%
tidyr::pivot_longer(-Name, names_to = "year") %>%
mutate(year = gsub("^x", "", year)) %>%
group_by(Name) %>%
mutate(new = as.numeric(value == 1 & lag(value) == 0),
new = ifelse(is.na(new), value, new)) %>%
ungroup() %>%
group_by(year) %>%
summarise(total = sum(value), new = sum(new))
#> # A tibble: 3 x 3
#> year total new
#> <chr> <dbl> <dbl>
#> 1 2017 1 1
#> 2 2018 3 2
#> 3 2019 3 1
mutate(new = as.numeric(values == 1 & lag(values) == 0), new = ifelse(is.na(new), values, new)) %>%
部分来自 stefan
(感谢他,谢谢斯特凡)。
区别是parse_number
library(tidyverse)
df %>%
pivot_longer(
cols = -Name,
names_to = "Year",
values_to = "values"
) %>%
mutate(Year = parse_number(Year)) %>%
mutate(new = as.numeric(values == 1 & lag(values) == 0),
new = ifelse(is.na(new), values, new)) %>%
group_by(Year) %>%
summarise(Total = sum(values), New = sum(new))
输出:
Year Total New
* <dbl> <dbl> <dbl>
1 2017 1 1
2 2018 3 2
3 2019 3 1
更新 02
我很抱歉,因为我不得不修改我的解决方案,因为我意识到只有当一个名字在前一年出现时,它才不被认为是新的,所以您也可以将它用于您的样本数据和页面下方显示的数据:
library(dplyr)
library(purrr)
df %>%
summarise(across(2:4, ~ sum(.x))) %>%
bind_cols() %>%
pivot_longer(everything(), names_to = "Year", values_to = "Total",
names_prefix = "x") %>%
left_join(df %>% select(2:4) %>% pmap_dfr(~ {x <- c(...); x - lag(x, default = 0)}) %>%
summarise(across(everything(), ~ sum(.x == 1))) %>%
pivot_longer(everything(), names_to = "Year", values_to = "New",
names_prefix = "x"),
by = "Year")
# A tibble: 3 x 3
Year Total New
<chr> <dbl> <dbl>
1 2017 1 1
2 2018 3 2
3 2019 3 1
case-I 当只需要检查前一行的记录时。
df %>%
pivot_longer(!Name, names_to = 'Year', names_prefix = 'x') %>%
group_by(Year) %>%
summarise(total = sum(value),
new = list(Name[value == 1]), .groups = 'drop') %>%
mutate(new = map2_int(new, lag(new), ~ sum(!(.x %in% .y))))
# A tibble: 3 x 3
Year total new
<chr> <dbl> <int>
1 2017 1 1
2 2018 3 2
3 2019 3 1
情况 II,当记录必须查看所有以前的行时。同时使用 map_*
和 accumulate
。采用的策略-
pivot_longer
先。使用 names_prefix
参数 直接从 years 中删除 x
group_by
当年
- 在
list
中计算当年的总值 n()
和名称
- mutate
new
using map2_int
仅将第一个参数作为该列表,将第二个参数作为 accumulate
d 和 lag
ged 列表。
map2_int
从而计算该行中 TRUE
的总数。
library(tidyverse)
df %>%
pivot_longer(!Name, names_to = 'Year', names_prefix = 'x') %>%
group_by(Year) %>%
summarise(total = sum(value),
new = list(Name[value == 1]), .groups = 'drop') %>%
mutate(new = map2_int(new, lag(accumulate(new, union, .init = first(new))[-1]), ~ sum(!(.x %in% .y))))
#> # A tibble: 3 x 3
#> Year total new
#> <chr> <int> <int>
#> 1 2017 1 1
#> 2 2018 3 2
#> 3 2019 3 1
我有一个名字和年份的数据框,有一个虚拟变量来表示名字是否出现在一年中。
我正在尝试创建一个数据框来告诉我
-
- 当年出现的名字总数,
-
- 当年出现而前年未出现的数量
在下面的例子中,2017 年只有一个人(Terry)出现,而前一年没有出现,因此 total 和 new 都是 1。2018 年出现了三个人,但只有两个是新人,因为 Terry 出现了在前一年。如果有人在2017年和2019年出现,但在2018年没有出现,那么他们应该被归类为2019年的新人。
例子
Name x2017 x2018 x2019
1 Terry 1 1 0
2 Sam 0 0 1
3 Nic 0 1 1
4 Sarah 0 1 1
代码
data.frame(
Name = c("Terry", "Sam", "Nic", "Sarah"),
x2017 = c(1, 0, 0, 0),
x2018 = c(1, 0, 1, 1),
x2019 = c(0, 1, 1, 1)
)
我正在尝试创建的输出
Year Total New
1 2017 1 1
2 2018 3 2
3 2019 3 1
我试过过滤和使用行总和,但我觉得有一个我不知道的函数可以做到这一点。
谢谢!
也许这就是您要找的:
- 重塑为长格式,例如使用
tidy::pivot_longer
- 分组
Name
并利用dplyr::lag
添加一个人是否是新人的指标 - 按年份汇总
d <- data.frame(
Name = c("Terry", "Sam", "Nic", "Sarah"),
x2017 = c(1, 0, 0, 0),
x2018 = c(1, 0, 1, 1),
x2019 = c(0, 1, 1, 1)
)
library(dplyr)
library(tidyr)
d %>%
tidyr::pivot_longer(-Name, names_to = "year") %>%
mutate(year = gsub("^x", "", year)) %>%
group_by(Name) %>%
mutate(new = as.numeric(value == 1 & lag(value) == 0),
new = ifelse(is.na(new), value, new)) %>%
ungroup() %>%
group_by(year) %>%
summarise(total = sum(value), new = sum(new))
#> # A tibble: 3 x 3
#> year total new
#> <chr> <dbl> <dbl>
#> 1 2017 1 1
#> 2 2018 3 2
#> 3 2019 3 1
mutate(new = as.numeric(values == 1 & lag(values) == 0), new = ifelse(is.na(new), values, new)) %>%
部分来自 stefan
(感谢他,谢谢斯特凡)。
区别是parse_number
library(tidyverse)
df %>%
pivot_longer(
cols = -Name,
names_to = "Year",
values_to = "values"
) %>%
mutate(Year = parse_number(Year)) %>%
mutate(new = as.numeric(values == 1 & lag(values) == 0),
new = ifelse(is.na(new), values, new)) %>%
group_by(Year) %>%
summarise(Total = sum(values), New = sum(new))
输出:
Year Total New
* <dbl> <dbl> <dbl>
1 2017 1 1
2 2018 3 2
3 2019 3 1
更新 02 我很抱歉,因为我不得不修改我的解决方案,因为我意识到只有当一个名字在前一年出现时,它才不被认为是新的,所以您也可以将它用于您的样本数据和页面下方显示的数据:
library(dplyr)
library(purrr)
df %>%
summarise(across(2:4, ~ sum(.x))) %>%
bind_cols() %>%
pivot_longer(everything(), names_to = "Year", values_to = "Total",
names_prefix = "x") %>%
left_join(df %>% select(2:4) %>% pmap_dfr(~ {x <- c(...); x - lag(x, default = 0)}) %>%
summarise(across(everything(), ~ sum(.x == 1))) %>%
pivot_longer(everything(), names_to = "Year", values_to = "New",
names_prefix = "x"),
by = "Year")
# A tibble: 3 x 3
Year Total New
<chr> <dbl> <dbl>
1 2017 1 1
2 2018 3 2
3 2019 3 1
case-I 当只需要检查前一行的记录时。
df %>%
pivot_longer(!Name, names_to = 'Year', names_prefix = 'x') %>%
group_by(Year) %>%
summarise(total = sum(value),
new = list(Name[value == 1]), .groups = 'drop') %>%
mutate(new = map2_int(new, lag(new), ~ sum(!(.x %in% .y))))
# A tibble: 3 x 3
Year total new
<chr> <dbl> <int>
1 2017 1 1
2 2018 3 2
3 2019 3 1
情况 II,当记录必须查看所有以前的行时。同时使用 map_*
和 accumulate
。采用的策略-
pivot_longer
先。使用names_prefix
参数 直接从 years 中删除 group_by
当年- 在
list
中计算当年的总值 - mutate
new
usingmap2_int
仅将第一个参数作为该列表,将第二个参数作为accumulate
d 和lag
ged 列表。 map2_int
从而计算该行中TRUE
的总数。
x
n()
和名称
library(tidyverse)
df %>%
pivot_longer(!Name, names_to = 'Year', names_prefix = 'x') %>%
group_by(Year) %>%
summarise(total = sum(value),
new = list(Name[value == 1]), .groups = 'drop') %>%
mutate(new = map2_int(new, lag(accumulate(new, union, .init = first(new))[-1]), ~ sum(!(.x %in% .y))))
#> # A tibble: 3 x 3
#> Year total new
#> <chr> <int> <int>
#> 1 2017 1 1
#> 2 2018 3 2
#> 3 2019 3 1