【问题标题】:Selecting a sample to match the distribution of variables in another dataset选择一个样本来匹配另一个数据集中的变量分布
【发布时间】:2020-06-18 17:41:29
【问题描述】:

让 x 是一个包含 5 个变量和 15 个观测值的数据集:

age gender  height  weight  fitness
17  M   5.34    68  medium
23  F   5.58    55  medium
25  M   5.96    64  high
25  M   5.25    60  medium
18  M   5.57    60  low
17  F   5.74    61  low
17  M   5.96    71  medium
22  F   5.56    75  high
16  F   5.02    56  medium
21  F   5.18    63  low
20  M   5.24    57  medium
15  F   5.47    72  medium
16  M   5.47    61  high
22  F   5.88    73  low
18  F   5.73    62  medium

适应度变量值的频率如下: 低 = 4,中 = 8,高 = 3。

假设我有另一个数据集 y,它具有相同的 5 个变量但有 100 个观察值。该数据集中适应度变量值的频率如下: 低 = 42,中 = 45,高 = 13。

使用 R,我如何从 y 中获得一个有代表性的样本,使得样本适应度与 x 中的适应度分布紧密匹配?

我最初的想法是使用 R 中的示例函数并为 prob 参数分配加权概率。然而,使用概率将强制频率分布精确匹配。我的目标是在最大化样本量的同时获得足够接近的匹配。

另外,假设我希望添加另一个约束条件,其中性别分布也必须与 x 的分布紧密匹配?

【问题讨论】:

  • 我认为您最多可以从 y 中采样 22、45 和 17 个,总共 84 个(在 100 个中)。这给出了 0.26、0.54 和 0.20 的比例,与 x (0.27, 0.53, 0.20) 的比例非常接近。
  • 但是我该如何执行这个并让 R 为我做采样呢?另请注意,我不可能采样 17 个高适应度值,因为最大值为 13。无论如何,样本分布不一定要那么接近,只要足以通过 x 的代表性样本即可。我想样本的大小对我来说并不像实际上首先获得样本那样紧迫。我也意识到,我设置的约束越多,样本量就会越小。
  • 啊,是的。我忘了添加那个约束。因此,通过从每个中减去一定数量来调整 84,然后重新计算样本大小,得到 18、35 和 13,它们的比例分别为 0.27、0.53 和 0.20。

标签: r sample frequency-distribution


【解决方案1】:

您的 y 中的最小频率为 13,对应于“高”健身水平。所以你不能采样超过这个数字。这是你的第一个约束。你想最大化你的样本量,所以你对所有 13 个样本都进行了抽样。为了匹配 x 中的比例,13 应该是你总数的 20%,这意味着你的总数必须是 65 (13/0.2)。因此,其他频率必须为 17(低)和 35(中等)。由于您的 y 中有足够的这些健康水平,您可以将其作为您的样本。如果任何其他采样频率超过了 y 中的数字,那么您将有另一个约束并且必须相应地调整这些。

对于抽样,您首先要选择所有具有“高”适应度的记录(确定抽样)。接下来,分别从其他级别进行抽样(分层随机抽样)。最后,将这三者结合起来。

例子:

rm(list=ls())
# set-up the data (your "y"):
df <- data.frame(age=round(rnorm(100, 20, 5)), 
                 gender=factor(gl(2,50), labels=LETTERS[c(6, 13)]), 
                 height=round(rnorm(100, 12, 3)), 
                 fitness=factor(c(rep("low", 42), rep("medium", 45), rep("high", 13)), 
                                levels=c("low","medium","high")))

为采样创建子集:

fit.low <- subset(df, subset=fitness=="low")
fit.medium <- subset(df, subset=fitness=="medium")
fit.high <- subset(df, subset=fitness=="high")

低适应度组的样本 17(占总数的 40.5% 或 26.7%)。

fit.low_sam <- fit.low[sample(1:42, 17),]

中等体能组的样本 35(占总数的 77.8% 或 53.8%)。

fit.med_sam <- fit.medium[sample(1:45, 35),]

将它们全部组合起来。

fit.sam <- rbind(fit.low_sam, fit.med_sam, fit.high)

我尝试使用 dplyr 的 sample_nsample_frac 函数来执行此操作,但我认为这些函数不允许您以不同比例进行分层抽样。

library(dplyr)
df %>%
  group_by(fitness) %>%
  sample_n(size=c(17,35,13), weight=c(0.27, 0.53, 0.2))
# Error

但是 sampling 包当然可以做到这一点。 Stratified random sampling from data frame

library(sampling)
s <- strata(df, "fitness", size=c(17,35,13), "srswor")
getdata(df, s)

【讨论】:

  • 是的,我现在明白多了!问题确实是分层抽样之一,因此感谢您推荐抽样包。
【解决方案2】:

考虑使用rmultinom 来准备每个适应度级别的样本计数。

准备数据(我已经使用@Edward 回答中的y 准备)

x <- read.table(text = "age gender  height  weight  fitness
17  M   5.34    68  medium
23  F   5.58    55  medium
25  M   5.96    64  high
25  M   5.25    60  medium
18  M   5.57    60  low
17  F   5.74    61  low
17  M   5.96    71  medium
22  F   5.56    75  high
16  F   5.02    56  medium
21  F   5.18    63  low
20  M   5.24    57  medium
15  F   5.47    72  medium
16  M   5.47    61  high
22  F   5.88    73  low
18  F   5.73    62  medium", header = TRUE)

y <- data.frame(age=round(rnorm(100, 20, 5)), 
                 gender=factor(gl(2,50), labels=LETTERS[c(6, 13)]), 
                 height=round(rnorm(100, 12, 3)), 
                 fitness=factor(c(rep("low", 42), rep("medium", 45), rep("high", 13)), 
                                levels=c("low","medium","high")))

现在采样程序: UPD:我已经更改了两个变量案例(性别和健身)的代码

library(tidyverse)

N_SAMPLES = 100

# Calculate frequencies
freq <- x %>%
    group_by(fitness, gender) %>% # You can set any combination of factors
    summarise(freq = n() / nrow(x)) 

# Prepare multinomial distribution
distr <- rmultinom(N_SAMPLES, 1, freq$freq)
# Convert to counts
freq$counts <- rowSums(distr)

# Join y with frequency for further use in sampling
y_count <- y %>% left_join(freq)

# Perform sampling using multinomial distribution counts
y_sampled <- y_count %>%
    group_by(fitness, gender) %>% # Should be the same as in frequencies calculation
    # Check if count is greater then number of observations
    sample_n(size = ifelse(n() > first(counts), first(counts), n()),
        replace = FALSE) %>%
    select(-freq, -counts)

【讨论】:

  • 当我运行 summarise(freq = n() / nrow(x)) 时,我收到一个错误:n() 只能在数据上下文中调用。
  • 没关系,我发现这只是由于包冲突,因为我之前加载了 dplyr。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-17
  • 1970-01-01
  • 2014-05-12
  • 1970-01-01
相关资源
最近更新 更多