我正在使用 R 中的数据集。该数据集包含一些值列和一些城市列,每个城市作为虚拟变量(0 和 1)。数据集是这样的:
df<-data.frame(A=c(1,2,2,3,4,5,1,1,2,3,4,4),
B=c(4,4,2,3,4,2,1,5,2,2,5,1) ,
C=c(rep(0:1, each=3, times=2)),
D=round(rnorm(12, mean=50, sd=10), 2) ,
City1=c(rep(0:1, each=6)),
City2=c(rep(c(1, 0), c(6,6))))
以上数据集是一个原型。真实的数据集有不同数量的“城市”变量,即有时一个数据集有 2 个“城市”列,有时它有 10 个“城市”列。 我想要一个解决方案,我可以根据每个“城市”的值创建单独的数据集。例如,代码基于“City1”列中的“1”值(而非“0”值)创建数据集,并存储在名称为“City1”的数据框中。然后,转到“City2”列并根据“City2”列中的“1”值(而非“0”值)创建数据集,并存储在名称为“City2”的单独数据框中。等等。
我知道像下面这样的一些代码可以完成这项工作,但这样我每次都必须根据“城市”变量的名称编写代码,而且每个数据集中的城市数量也不同。
df1 <- df[df$City1==1,]
df2 <- df[df$City2==1,]
有人可以帮我解决这个问题吗? 提前谢谢你。
识别城市列,然后遍历它们并split:
cc <- which(grepl("^City", colnames(df)))
lapply(cc, function(i){ split(df[, -cc], df[ i ]) })
这是一种使用
purrr::map
和 rlang::bind_env
的方法。这会在全局环境中创建 df1
和 df2
,注意不要覆盖现有对象!如果您只想要一个 data.frame
的列表,那么只需使用 map
即可。
library(purrr)
library(rlang)
grep("City", names(df), value = TRUE) %>%
set_names() %>%
map(~ df[df[[.x]] == 1, ]) %>%
env_bind(.GlobalEnv, !!! .)
来自 OP 的数据
df <- data.frame(A = c(1,2,2,3,4,5,1,1,2,3,4,4),
B = c(4,4,2,3,4,2,1,5,2,2,5,1),
C = c(rep(0:1, each=3, times=2)),
D = round(rnorm(12, mean=50, sd=10), 2),
City1 = c(rep(0:1, each=6)),
City2 = c(rep(c(1, 0), c(6,6)))
)
由 reprex 包 (v2.0.1) 于 2023-03-07 创建
您可以
paste
列然后split
:
Citys <- startsWith(colnames(df), "City")
split(df, do.call("paste", df[Citys]))
或者,与
pivot_longer
:
library(tidyr)
library(dplyr)
df %>%
pivot_longer(starts_with("City"), names_to = "Cities") %>%
filter(value == 1) %>%
split(.$Cities)
如果要将列表转换为全局环境中的多个数据框,请使用
list2env(your_list, .GlobalEnv)
。
您可以使用
df
为以 City 开头的列子集 startsWith
,测试它们是否等于 1 == 1
并获得 max.col
的列。 Paste
df 在列的前面并使用它来 split
df
。使用list2env
获得全局环境中的data.frames
。
list2env(split(df, paste0("df", max.col(df[startsWith(names(df), "City")] ==
1))), globalenv())
df1
# A B C D City1 City2
#7 1 1 0 65.30 1 0
#8 1 5 0 45.81 1 0
#9 2 2 0 43.37 1 0
#10 3 2 1 55.14 1 0
#11 4 5 1 59.21 1 0
#12 4 1 1 50.55 1 0
df2
# A B C D City1 City2
#1 1 4 0 62.32 0 1
#2 2 4 0 45.78 0 1
#3 2 2 0 54.80 0 1
#4 3 3 1 44.96 0 1
#5 4 4 1 61.42 0 1
#6 5 2 1 51.26 0 1
如果要将其保存在列表中并假设 City 仅用 0 或 1 编码,您可以尝试:
split(df, max.col(df[startsWith(names(df), "City")]))
或使用
lapply
和子集df
.
lapply(df[startsWith(names(df), "City")], \(i) df[i==1,])
基准
bench::mark(check = FALSE,
zx8754 = {cc <- which(grepl("^City", colnames(df))) #Returns something different
lapply(cc, function(i){ split(df[, -cc], df[ i ]) })},
TimTeaFan = {grep("City", names(df), value = TRUE) %>%
set_names() %>%
map(~ df[df[[.x]] == 1, ])},
Maël = split(df, do.call("paste", df[startsWith(colnames(df), "City")])),
GKi = split(df, max.col(df[startsWith(names(df), "City")])),
GKi2 = lapply(df[startsWith(names(df), "City")], \(i) df[i==1,])
)
# expression min median itr/s…¹ mem_al…² gc/se…³ n_itr n_gc total…⁴ result
# <bch:expr> <bch:tm> <bch:> <dbl> <bch:by> <dbl> <int> <dbl> <bch:t> <list>
#1 zx8754 495µs 548µs 1621. 11.27KB 10.3 788 5 486ms <NULL>
#2 TimTeaFan 226µs 247µs 3863. 0B 12.3 1877 6 486ms <NULL>
#3 Maël 250µs 264µs 3754. 0B 12.3 1824 6 486ms <NULL>
#4 GKi 302µs 321µs 3051. 240B 12.4 1480 6 485ms <NULL>
#5 GKi2 161µs 177µs 5575. 6.36KB 14.5 2694 7 483ms <NULL>