如何根据某些列自动对数据框进行子集化并存储在 R 中的单独 dfs 中

问题描述 投票:0回答:4

我正在使用 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,]

有人可以帮我解决这个问题吗? 提前谢谢你。

r subset
4个回答
2
投票

识别城市列,然后遍历它们并split

cc <- which(grepl("^City", colnames(df)))
lapply(cc, function(i){ split(df[, -cc], df[ i ]) })

2
投票

这是一种使用

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 创建


1
投票

您可以

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)


0
投票

您可以使用

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>
© www.soinside.com 2019 - 2024. All rights reserved.