【问题标题】:Advice on a loop function to subset data according to variables关于根据变量对数据进行子集化的循环函数的建议
【发布时间】:2021-03-19 20:00:10
【问题描述】:

我有一个包含 453 个变量(列)和 119 个观察值(行)的数据集。它由多年来针对不同国家的 118 项健康观察组成。例如,453 个变量中有 10 个包含澳大利亚过去 10 年的健康数据; 453 个变量中有 8 个包含孟加拉国过去 8 年的健康数据。

我想将这 453 个变量子集到他们自己的基于国家/地区的数据框中。国家名称和年份在第 1 行(例如 Australia_2013、Australia_2014 等)。看到这个数据集中似乎有超过 40 个国家,我想为此创建一个循环。

从我目前阅读的内容来看,我认为我应该创建一个国家名称的向量列表,然后编写一个循环函数,根据向量列表对数据进行子集化。然而,我能找到的所有示例都是基于行的子集。

谁能指出我正确的方向,或为此分享示例代码?

非常感谢期待

【问题讨论】:

  • 请提供一小部分数据样本。如果我们有一个数据集可以使用(并且我们还可以看到您正在使用的数据结构),那么解决这个问题会更容易。它不一定是实际数据。仅包括您认为有用的列。 See here if you need help making a reproducible dataset as an example.
  • 如果您发现以下任何答案有用,请将其标记为“已解决”。谢谢!
  • @LC-datascientist - 也很抱歉没有提供示例数据集。下面你回答的例子是我所拥有的可比较的版本。

标签: r loops subset


【解决方案1】:

根据您的描述,我假设您的数据如下所示:

country_year <- c("Australia_2013", "Australia_2014", "Bangladesh_2013")
health <- matrix(nrow = 3, ncol = 3, data = runif(9))
dataset <- data.frame(rbind(country_year, health), row.names = NULL, stringsAsFactors = FALSE)

dataset
#                 X1                X2                 X3
#1    Australia_2013    Australia_2014    Bangladesh_2013
#2 0.665947273839265 0.677187719382346  0.716064820764586
#3 0.499680359382182 0.514755881391466  0.178317369660363
#4 0.730102791683748 0.666969108628109 0.0719663293566555

首先,move your row 1 (e.g., Australia_2013, Australia_2014 etc.) to the column names,然后应用循环创建基于国家/地区的数据框。

library(dplyr)

# move header
dataset2 <- dataset %>% 
    `colnames<-`(dataset[1,]) %>%  # uses row 1 as column names
    slice(-1) %>% # removes row 1 from data
    mutate_all(type.convert) # converts data to appropriate type

# apply loop
for(country in unique(gsub("_\\d+", "", colnames(dataset2)))) {
    assign(country, select(dataset2, starts_with(country))) # makes subsets
}

关于循环,

gsub("_\\d+", "", colnames(dataset2)) 通过将“_[year]”替换为空(即删除它)来提取国家/地区名称,并且应用的 unique() 函数提取每个国家/地区名称之一。

assign(country, select(dataset2, starts_with(country))) 创建一个以国家/地区命名的变量,该国家/地区变量仅包含 dataset2 中以国家/地区名称开头的列。

编辑:回复评论

评论中的问题是询问如何在基于国家/地区的数据框中添加逐行摘要(例如,rowSums()rowMeans())作为新列,同时使用此 for 循环。

这是一种需要最少更改的解决方案:

for(country in unique(gsub("_\\d+", "", colnames(dataset2)))) {
    assign(country, 
        select(dataset2, starts_with(country)) %>% # makes subsets
            mutate( # creates new columns
                rowSums = rowSums(select(., starts_with(country))),
                rowMeans = rowMeans(select(., starts_with(country)))
            )
    )
}

mutate() 向数据集添加新列。

select(., starts_with(country)) 从当前对象中选择以国家名称开头的列(在函数中表示为.)。

【讨论】:

  • 非常感谢!我可以很好地将它与我现有的数据集集成。我也非常感谢您的解释。
  • 我可以问一个较小的后续问题 - 我想为每个新数据子集创建一个汇总列 - 计算年份的总数和平均值。使用来自 dataset2 的澳大利亚示例,我应用了以下代码: i
  • @Rebecca - 是的,我附加了答案来解决您的问题。
【解决方案2】:

这是一个dplyr 的答案,版本 >= 1.0。

我创建了一个小例子,我们将不同的列嵌套到data 列中。然后由于nest_by 已经创建了一个rowwise 分组,我们可以为以国家名称开头的列设置每个data 的子集。我们需要将其转换为字符。 最后,如果需要,您可以拉列表列subset 以获取包含相关列的小标题列表。 值得注意的是,我认为使用整洁的格式(长而不是双重信息编码(国家和年份)会更容易。

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

df <- data.frame(country = c("A", "B", "C"),
                 A_1 = 1:3,
                 A_2 = 3:5,
                 B_1 = 2:4,
                 C_3 = 1:3)
df
#>   country A_1 A_2 B_1 C_3
#> 1       A   1   3   2   1
#> 2       B   2   4   3   2
#> 3       C   3   5   4   3
nest_by(df, country) %>% 
  mutate(subset = list(select(data, starts_with(as.character(country))))) %>% 
  pull(subset)
#> [[1]]
#> # A tibble: 1 x 2
#>     A_1   A_2
#>   <int> <int>
#> 1     1     3
#> 
#> [[2]]
#> # A tibble: 1 x 1
#>     B_1
#>   <int>
#> 1     3
#> 
#> [[3]]
#> # A tibble: 1 x 1
#>     C_3
#>   <int>
#> 1     3

reprex package (v0.3.0) 于 2020 年 12 月 8 日创建

【讨论】:

  • 感谢您到目前为止的帮助。两个简短的跟进:首先,我收到一条错误消息:nest_by(df, country) 中的错误:找不到函数“nest_by”。 dplyr 以与您相同的方式连接。其次,这是否取决于国家名称列(因为数据目前没有)。
  • 如上所述,您需要dplyr 版本>=1.0 才能获得nest_by。是的,第一步是有一个包含国家描述的列,我虽然你有它。然后我会采用pivot_longer 方法
【解决方案3】:

首先,数据结构不是最优的,第一行作为字符串表示,所有其他行(每列)中的数字也被 R 编码为字符串。但这不是问题的一部分。

您不能创建一系列数据框,但如果它们是列表的一部分(这就是 R 中的列表!),您可以创建一个列表元素包含一个国家/地区。 p>

纯基础 R 方法,带有工作示例的解决方案:

# example dataset df
data("mtcars")
df <- mtcars
df <- rbind(paste0(sample(letters, ncol(df), replace = TRUE), "_2014"), df)
str(df)

# solution
countries <- substr(df[1, ], 1, nchar(df[1, ]) - 5)
unique_countries <- unique(countries)
df <- rbind.data.frame(countries, df, stringsAsFactors = FALSE)

list_df_per_country <- list()

for (i in seq_along(unique_countries)) {
  list_df_per_country[[i]] <- df[which(df[1, ] == unique_countries[i])]
}

要使用上面的代码,只需将数据框保存为 df,即df &lt;- your_dataframe,然后逐行运行# solution 下面的行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    • 2013-08-15
    • 2020-11-30
    相关资源
    最近更新 更多