我有一个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)).