子集R数据帧由一行的条件值组成

时间:2017-05-02 15:22:03

标签: r

好的,所以我被困在这里......对R来说相当新。我有一个类似于下面的数据框。

让我们说他们是学生的考试成绩......

set.seed(8675309)
name <- c('Bil', 'Sal', 'Kai', 'Kat', 'Jim', 'Xiu')
age <- c(7, 8, 8, 9, 8, 7)
HAVE <- data.frame(name, age, matrix(sample(100, 6*6, TRUE), ncol=6))

我希望我的代码能做两件事:

1. Given a name, find the best three "tests" for that student.
and...
2. Then pull all the data for only those three tests

例如,我想看看Bil ......

pick_name <- 'Bil'

我尝试使用这段代码,为每位学生提供最佳考试成绩......虽然它似乎没有正常运作。

t3 <- colnames(HAVE)[max.col(HAVE[3:8], ties.method = 'first')]
t3 <- colnames(HAVE[which(HAVE$name==pick_name)])[max.col(HAVE[3:8], ties.method = 'first')]

Bil最好的三个测试是X2,X4和X6,所以我想要的输出应该是这样的:

WANT <- subset(HAVE, select = c(name, age, X2, X4, X6))

我对SAS更熟悉,我这样做的方法是将所需的变量拉入一个列表并将其分配给一个宏变量,然后基于该宏变量的列表进行子集...但不确定如何在R中这样做。

3 个答案:

答案 0 :(得分:2)

您可以直接找出索引和子集。它将涉及识别具有给定名称(HAVE$name == "Bil")的行,按降序排序(order(..., decreasing = TRUE)),访问前三个索引([1:3])和子设置{{1基于这些值。

HAVE

答案 1 :(得分:1)

..但是SAS(?)中没有列表,在我看来类似于proc sql和proc transpose的方式(注意你必须安装tidyr&amp; dplyr):

library(tidyr)
library(dplyr)
HAVE_long<-gather(HAVE,key="X",value="Score",X1:X6) # like proc transpose
Bil_score<-HAVE_long %>%    # a bit like proc sql
  filter(name=="Bil") %>%
  top_n(3,Score)
Bil_score

答案 2 :(得分:1)

此问题已有两个答案,但两者都是恕我直言不完整

两者都只适用于"Bil"(硬编码!)并且不提供通用解决方案。使用tidyrdplyr的{​​{3}}完全无法满足OP的第二个要求(然后仅为这三个测试提取所有数据)。< / p>

OP正在寻找类似SAS的方法,并提到列表宏变量

增强的碱基R溶液

可以增强聪明的基础R answer,以返回包含每个可能pick_name的结果的列表:

result <- lapply(name, 
                 function(pick_name) {
                   HAVE[, c(1:2, sort(order(HAVE[HAVE$name == pick_name, -(1:2)], 
                                            decreasing = TRUE)[1:3] + 2))]
                 }
)
result <- setNames(result, name)

result
#$Bil
#  name age X2 X4 X6
#1  Bil   7 98 82 98
#2  Sal   8 85 72 85
#3  Kai   8 86  7 33
#4  Kat   9 45 85 60
#5  Jim   8 84 85 19
#6  Xiu   7 59 34 46
#
#$Sal
#  name age X2 X4 X6
#1  Bil   7 98 82 98
#2  Sal   8 85 72 85
#3  Kai   8 86  7 33
#4  Kat   9 45 85 60
#5  Jim   8 84 85 19
#6  Xiu   7 59 34 46
#
#$Kai
#  name age X1 X2 X3
#1  Bil   7 16 98 52
# ...
#$Xiu
#  name age X1 X2 X5
#1  Bil   7 16 98 56
#2  Sal   8 48 85 67
#3  Kai   8 77 86 26
#4  Kat   9 77 45  8
#5  Jim   8 27 84 16
#6  Xiu   7 68 59 82

result是一个列表(在R术语中),其中包含每个pick_name数据框,其中包含与所有其他名称相比的前3个测试的数据。 lapply()是一种隐含的&#34; for循环遍历name返回列表的所有元素。 setNames()重命名result列表的每个元素。

可以按编号或名称选择单个列表元素。因此,

result$Jim
result[[5]]

都返回相同的列表元素:

#  name age X2 X3 X4
#1  Bil   7 98 52 82
#2  Sal   8 85 27 72
#3  Kai   8 86 75  7
#4  Kat   9 45 92 85
#5  Jim   8 84 72 85
#6  Xiu   7 59 21 34

更强大的解决方案

基础R解决方案(虽然无疑是一个聪明的解决方案)非常脆弱,因为它对初始HAVE数据框中列的数量和位置做出了隐含的假设。例如,如果要添加列gender,它将失败。或者,如果需要考虑不同数量的顶级测试,例如,最好的4个测试而不是最好的3个测试。

以下方法使用列名更加健壮。它只需要包含测试结果的列始终由&#34; X&#34;后跟一个数字,例如X12

为了使过程独立于相关列的数量和位置,数据从宽格式转换为长格式。在长格式中,测试的名称(由列名称给出)被视为数据。要进行重新整形,请使用melt()包的cast()data.table

library(data.table)   # version 1.10.4. used
# reshape from wide to long
molten <- melt(as.data.table(HAVE), measure.vars =  patterns("^X\\d+$"))

重新塑造的数据如下所示:

molten
#    name age variable value
# 1:  Bil   7       X1    42
# 2:  Sal   8       X1    79
# 3:  Kai   8       X1     1
# 4:  Kat   9       X1    29
# 5:  Jim   8       X1    18
# 6:  Xiu   7       X1    77
# 7:  Bil   7       X2    93
# 8:  Sal   8       X2    32
# 9:  Kai   8       X2    18
# ...
#30:  Xiu   7       X5    95
#31:  Bil   7       X6    85
#32:  Sal   8       X6    10
#33:  Kai   8       X6    53
#34:  Kat   9       X6    97
#35:  Jim   8       X6    10
#36:  Xiu   7       X6    26
#    name age variable value    

最后,使用lapply()setNames()创建结果列表,就像上面的增强型基础R解决方案一样。

# specify the number of top tests
n_top <- 3
result <- setNames(
  lapply(name, function(pick_name) {
    # get names of n_top best test results for pick_name
    cols <- molten[name == pick_name][order(-value), head(variable, n_top)]
    # reshape from long to wide but use only selected tests
    dcast(molten, name + age + gender ~ ..., subset = .(variable %in% cols))
  }), name
)

result与上面显示的相同。