如何解决使用来自R
这是我第一次在这里发布问题,所以如果我不遵循某些准则,请告诉我,我将立即对其进行更改。
基本上,我的问题如下:我有两个数据集(为简单起见,分别称为数据集A和数据集B)由一系列共同的列组成,这些列包括每个人/观察/的社会人口统计特征行。我需要的是数据集A中的每个观测/行,我必须从数据集B中选择一个随机观测,该观测具有与关键的社会人口统计变量有关的匹配特征。为了说明,我准备了一个简单的示例:
library("dplyr")
A = data.frame(nuts2 = c(1,1,2,3,3),gender = c(1,2))
B = data.frame(nuts2 = c(rep(1,10),rep(2,rep(3,10)),gender=c(rep(1,5),rep(1,5)))
A <- A[sample(1:nrow(A)),] %>% mutate(id = seq(1:nrow(A)))
B <- B[sample(1:nrow(B)),] %>% mutate(id = seq(1:nrow(B)))
我正在尝试避免forloops,因为在R中这似乎被认为是不好的做法,因此我尝试创建一个函数并使用apply在每个观察值上运行它。假设我们希望匹配来自B的随机观察ID,该ID与A中的观察具有相同的性别和nuts2值,我的代码如下:
matching_fun <- function(x) {
donor <- B %>% filter(gender == A$gender & nuts2 == A$nuts2) %>% sample_n(1)
donor_id <- donor$id
return(donor_id)
}
A$donor_id <- apply(A,matching_fun)
我希望这会导致一个数据帧包含A中存在的所有信息和一个名为don_id的额外列,并通过B中的社会人口统计学小组的随机抽样识别出相应的供体ID。
但是,我的代码无法正确执行比赛,也没有遵守社会人口统计特征。谁能告诉我我在做什么错?
注意:我的数据集每个都有近200万个观测值,我将不得不在多个测试中使用它。因此,计算效率具有一定的重要性。
解决方法
这是一种data.table
方法
setDT(A); setDT(B)
A[B,on = .(nuts2,gender)][,.(id = i.id[[sample(.N,1L)]]),by = .(nuts2,gender)]
输出
> set.seed(1L)
> A[B,gender)]
nuts2 gender id
1: 1 2 1
2: 2 2 16
3: 2 1 4
4: 1 1 6
5: 3 2 25
6: 3 1 22
> A[B,gender)]
nuts2 gender id
1: 1 2 8
2: 2 2 10
3: 2 1 24
4: 1 1 5
5: 3 2 25
6: 3 1 29
> A[B,gender)]
nuts2 gender id
1: 1 2 8
2: 2 2 3
3: 2 1 4
4: 1 1 18
5: 3 2 25
6: 3 1 13
更新
我重新格式化了代码,以帮助您了解其背后的逻辑。
library(data.table)
setDT(A); setDT(B)
A[
B,gender)
][,.(id = id[[1L]],donor_id = i.id[[sample(.N,gender)
]
输出看起来像这样
nuts2 gender id donor_id
1: 2 1 1 15
2: 1 2 2 2
3: 3 2 5 8
4: 2 2 3 6
5: 1 1 4 9
6: 3 1 6 22
上面的代码本质上与下面的dplyr
管道相同,只是效率更高。
left_join(A,B,by = c("nuts2","gender")) %>% rename(id = id.x,donor_id = id.y) %>% group_by(nuts2,gender) %>% slice_sample(n = 1L)
如果仍然难以理解,请参见下图:
Let A and B be the dataframes as follows:
A B
nuts2 gender id nuts2 gender id
1 1 1 1 1 2
2 1 3 1 1 7
2 1 13
2 1 4
2 1 6
What you want to do is:
First,match these groups
A B
nuts2 gender id nuts2 gender id
1 1 1 1 1 2
1 1 7
--------------- ----------------
2 1 3 2 1 13
2 1 4
2 1 6
Second,slice a sample for each group in B
A B
nuts2 gender id nuts2 gender id
1 1 1 1 1 2
1 1 7 <--- RNG gives you this
--------------- ----------------
2 1 3 2 1 13
2 1 4 <--- RNG gives you this
2 1 6
Then,create a new variable in A with the id in B and call it donor_id
A B
nuts2 gender id donor_id nuts2 gender id
1 1 1 7 1 1 2
1 1 7 <--- RNG gives you this
------------------------ ----------------
2 1 3 4 2 1 13
2 1 4 <--- RNG gives you this
2 1 6
However,an equivalent but more efficient way is
First,join A and B on/by nuts2 and gender. In this way we can determine the population that we want to sample from.
A B
+---------------+ +----------------+ +----------------------+
|nuts2 gender id| |nuts2 gender id| |nuts2 gender id.A id.B|
| 1 1 1| | 1 1 2| by (nuts2,gender) | 1 1 1 2|
| 2 1 3| + | 1 1 7| =======> | 1 1 1 7|
+---------------+ | 2 1 13| | 2 1 3 13|
| 2 1 4| | 2 1 3 4|
| 2 1 6| | 2 1 3 6|
+----------------+ +----------------------+
Then,just slice a sample row within each (nuts2,gender) group and rename id.A and id.B as id and donor_id,respectively.
A + B
id donor_id
nuts2 gender XXXX XXXX
1 1 1 2
1 1 1 7 <--- RNG gives you this
--------------------------
2 1 3 13
2 1 3 4 <--- RNG gives you this
2 1 3 6
这就是我的代码。
这部分意味着在nuts2
和gender
的每个匹配组上加入A和B。
A[
B,gender)
]
然后,我们在.N
和nuts2
每组中的gender
行中切出一个示例行。 .N
是data.table
包中的保留字;它为您提供每个组中的行数。之所以有i.id
,是因为A和B都具有列id
,并且data.table
在加入之后自动将B的id
重命名为i.id
。另外,我们只需要id[[1L]]
,因为每个组中的id
是相同的。
A[
B,gender)
]
,
如果您想避免forloops,也许if / else循环会适合您:
library("dplyr")
#set.seed(1)
A = data.frame(nuts2 = c(1,1,2,3,3),gender = c(1,2))
B = data.frame(nuts2 = c(rep(1,10),rep(2,rep(3,10)),gender=c(rep(1,5),rep(1,5)))
A <- A[sample(1:nrow(A)),] %>% mutate(id = seq(1:nrow(A)))
B <- B[sample(1:nrow(B)),] %>% mutate(id = seq(1:nrow(B)))
if (is.element(A$gender,B$gender) & is.element(A$nuts2,B$nuts2)){
donor_id <- sample(B$id,6)
filter<-A[is.element(A$gender,B$nuts2),]
result<-cbind(filter,donor_id)[-3]
print(result)
}else{
print("No matching characteristics")
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。