选择样本以匹配另一个数据集中变量的分布
Selecting a sample to match the distribution of variables in another dataset
设 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 中的 sample 函数并为 prob 参数分配加权概率。但是,使用概率会强制精确匹配频率分布。我的 objective 是在最大化样本量的同时获得足够接近的匹配。
此外,假设我想添加另一个约束条件,其中性别分布也必须与 x 的分布紧密匹配?
y 中的最小频率为 13,对应于 "high" 健康水平。所以你不能采样超过这个数字。这是你的第一个约束。您想要最大化您的样本量,因此您对所有 13 个样本都进行了抽样。为了匹配 x 中的比例,13 应该占总数的 20%,这意味着您的总数必须为 65 (13/0.2)。因此,其他频率必须为 17(低)和 35(中等)。由于您的 y 中有足够的这些健康水平,您可以将其作为样本。如果任何其他样本频率超过 y 中的数字,那么您将有另一个约束并且必须相应地调整这些。
对于抽样,您首先 select 所有具有 "high" 适应性的记录(确定性抽样)。接下来,分别从其他级别抽样(分层随机抽样)。最后把三者结合起来。
示例:
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_n
和 sample_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)
考虑使用 rmultinom
准备每个健康水平的样本计数。
准备数据(我使用了y
来自@Edward回答的准备)
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")))
现在抽样程序:
更新:我已经更改了两个变量案例(性别和健康)的代码
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)
设 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 中的 sample 函数并为 prob 参数分配加权概率。但是,使用概率会强制精确匹配频率分布。我的 objective 是在最大化样本量的同时获得足够接近的匹配。
此外,假设我想添加另一个约束条件,其中性别分布也必须与 x 的分布紧密匹配?
y 中的最小频率为 13,对应于 "high" 健康水平。所以你不能采样超过这个数字。这是你的第一个约束。您想要最大化您的样本量,因此您对所有 13 个样本都进行了抽样。为了匹配 x 中的比例,13 应该占总数的 20%,这意味着您的总数必须为 65 (13/0.2)。因此,其他频率必须为 17(低)和 35(中等)。由于您的 y 中有足够的这些健康水平,您可以将其作为样本。如果任何其他样本频率超过 y 中的数字,那么您将有另一个约束并且必须相应地调整这些。
对于抽样,您首先 select 所有具有 "high" 适应性的记录(确定性抽样)。接下来,分别从其他级别抽样(分层随机抽样)。最后把三者结合起来。
示例:
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_n
和 sample_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)
考虑使用 rmultinom
准备每个健康水平的样本计数。
准备数据(我使用了y
来自@Edward回答的准备)
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")))
现在抽样程序: 更新:我已经更改了两个变量案例(性别和健康)的代码
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)