通过R在数据帧中基于二进制列对列中的闭合值进行子集化

我有一个85行和35列的数据框,根据年龄列排序,如下所示:

No  Gender  Age
1   F   5.8
2   F   5.9
3   F   6
4   M   6.2
5   F   7
6   F   7.2
7   M   7.4
8   M   7.8
9   M   7.9
10  M   8.1
11  F   8.3
12  F   8.6
13  M   8.9
14  M   9
15  F   9.2
16  F   9.3

我需要在不同性别中分配最接近的年龄.如下:

No  Gender  Age
1   F   6
2   M   6.2
3   F   7.2
4   M   7.4
5   M   8.1
6   F   8.3
7   F   8.6
8   M   8.9
9   M   9
10  F   9.2

最佳答案 好的,我想我得到了这个.这是非常困难的,也许其他人将能够提出一个更优雅的解决方案,但这就是我得到的:

df <- data.frame(No=c(1L,2L,3L,4L,5L,6L,7L,8L,9L,10L,11L,12L,13L,14L,15L,16L),Gender=c('F','F','F','M','F','F','M','M','M','M','F','F','M','M','F','F'),Age=c(5.8,5.9,6,6.2,7,7.2,7.4,7.8,7.9,8.1,8.3,8.6,8.9,9,9.2,9.3),stringsAsFactors=F);
mls <- df$Gender=='M';
mages <- df$Age[mls];
fages <- df$Age[!mls];
fisLower <- findInterval(mages,fages);
TOL <- 1e-5;
fisClosest <- fisLower+ifelse(fisLower==0L | fisLower<length(fages) & mages-fages[replace(fisLower,fisLower==0L,NA)]>fages[fisLower+1L]-mages+TOL,1L,0L);
mis <- unname(tapply(seq_along(mages),fisClosest,function(is) is[which.min(abs(mages[is]-fages[fisClosest[is[1L]]]))]));
fis <- unique(fisClosest);
df[sort(c(which(mls)[mis],which(!mls)[fis])),];
##    No Gender Age
## 3   3      F 6.0
## 4   4      M 6.2
## 6   6      F 7.2
## 7   7      M 7.4
## 10 10      M 8.1
## 11 11      F 8.3
## 12 12      F 8.6
## 13 13      M 8.9
## 14 14      M 9.0
## 15 15      F 9.2

变量解释:

> df输入data.frame.
> mls“male logicals”:一个逻辑向量,表示df $Gender的哪些元素是男性.
>法师“男性年龄”:男性行的df $年龄的子集.
> fages“女性年龄”:女性行的df $年龄子集.
> fisLower“女性指数降低”:对于法师的每个元素,它具有女性年龄的指数,该指数恰好低于(或可能等于)男性年龄.如果法师没有低于法师元素的年龄,这可能为零.因此,该向量与法师“平行”,意味着它具有相同的长度并且元素彼此对应.
> TOL“容差”这是在下面的语句中防止虚假浮点比较错误的必要烦恼.
> fisClosest“女性指数最接近”这是fisLower的简单转换.基本上,我们必须为fisLower的每个元素添加1L,如果mages的相应元素实际上更接近fages的后续元素(“upper”),而不是fisLower的相应元素所指向的元素(“lower”)一).这必须针对两种情况进行:(1)fisLower的零元素,以及(2)fisLower的元素指向fages的非最后元素,而mages的元素实际上更接近后来的fages元素.
>错误的“男性指数”首先,要了解fisClosest可能包含重复,如果多个男性年龄与他们最接近的女性年龄相同,IOW没有其他女性年龄接近男性年龄,对于他们所有人.对于这些冲突中的每一个,我们必须从男性年龄组中找到最接近女性年龄的男性年龄.这需要tapply()适合的向量聚合.我们通过fisClosest进行分组,将mages索引传递给lambda,我们在其中调用which.min()来获取年龄之间的绝对差异以获得获胜的男性年龄,并返回其索引.
> fis“女性指数”这只是我们需要从df中选择的一系列独特的索引指数;我们通过删除重复项从fisClosest得到这个.

在这一点上,我们最终可以通过索引相应的mls极性来将法师和fages索引(mis和fis)转换为df行索引.在对两个索引集进行组合和排序之后,我们最终可以将df索引以获得所需的输出.

原始(不正确)解决方案

看起来你想要每个运行长度的第一行和最后一行,除了整个data.frame的第一行和最后一行.这是实现这一目标的一种方法:

df <- data.frame(No=c(1L,2L,3L,4L,5L,6L,7L,8L,9L,10L,11L,12L,13L,14L,15L,16L),Gender=c('F','F','F','M','F','F','M','M','M','M','F','F','M','M','F','F'),Age=c(5.8,5.9,6,6.2,7,7.2,7.4,7.8,7.9,8.1,8.3,8.6,8.9,9,9.2,9.3),stringsAsFactors=F);
x <- cumsum(rle(df$Gender)$lengths); df2 <- df[unique(c(rbind(c(1L,x[-length(x)]+1L),x))),];
df2 <- df2[-c(1L,nrow(df2)),]; ## remove first and last row from original data.frame
df2;
##    No Gender Age
## 3   3      F 6.0
## 4   4      M 6.2
## 5   5      F 7.0
## 6   6      F 7.2
## 7   7      M 7.4
## 10 10      M 8.1
## 11 11      F 8.3
## 12 12      F 8.6
## 13 13      M 8.9
## 14 14      M 9.0
## 15 15      F 9.2

我想你错过了预期输出中的F 7.0行;除此之外,这将获得相同的行集.如果你想修复No从1顺序,你可以运行df2 $No< -seq_len(nrow(df2)).同名行名(在LHS上使用rownames(df2)).

点赞