2017-02-14 109 views
2

我有一个有点凌乱的数据框,其中的主题排名,但有些与排名并列。将子集的矢量值替换为子集的平均值

subj<-c("A","B","C,D,E","C,D,E","C,D,E","F","G,H","G,H","I") 
    rank<-c(1,2,3,4,5,6,7,8,9) 
    df<-data.frame(rank,subj) 
    df 
     rank subj 
    1 1  A 
    2 2  B 
    3 3 C,D,E 
    4 4 C,D,E 
    5 5 C,D,E 
    6 6  F 
    7 7 G,H 
    8 8 G,H 
    9 9  I 

如果个人被捆绑在一起,我需要将他们的排名表达为平局的平均值。喜欢的东西

n.rank n.subj 
1 1.0  A 
2 2.0  B 
3 4.0  C 
4 4.0  D 
5 4.0  E 
6 6.0  F 
7 7.5  G 
8 7.5  H 
9 9.0  I 

我一直在使用strngsplit()和队伍命名列表的元素试过,但我最终似乎同样难以对付的数据帧。

a<-strsplit(as.character(df$subj),",") 
names(a)<-df$rank 
b<-melt(a) 
colnames(b)<-c("n.subj","n.rank") 
b[1:10,] 
    n.subj n.rank 
1  A  1 
2  B  2 
3  C  3 
4  D  3 
5  E  3 
6  C  4 
7  D  4 
8  E  4 
9  C  5 
10  D  5 

我也达到一个死胡同,当我用gregexpr()regmatches(),试图找出需要进行平均行列。

m<-gregexpr(",+",df$subj) 
    df$no.avg<-melt(lapply(regmatches(df$subj, m),length))[,1]+1 
    df 
    rank subj no.avg 
    1 1  A  1 
    2 2  B  1 
    3 3 C,D,E  3 
    4 4 C,D,E  3 
    5 5 C,D,E  3 
    6 6  F  1 
    7 7 G,H  2 
    8 8 G,H  2 
    9 9  I  1 

那里有什么创意解决方案吗?非常感谢。

回答

3

这是我的尝试。我首先计算平均排名,然后将同一排名的主题分成若干行。

library(tidyverse) 
options(stringsAsFactors = FALSE) 
subj <- c("A", "B", "C,D,E", "C,D,E", "C,D,E", "F", "G,H", "G,H", "I") 
rank <- c(1, 2, 3, 4, 5, 6, 7, 8, 9) 
df <- data.frame(rank, subj) 

df %>% 
    group_by(subj) %>% 
    summarise(rank = mean(rank)) %>% 
    rowwise() %>% 
    do(tibble(subj = unlist(strsplit(.$subj, ",")), rank = .$rank)) %>% 
    ungroup() 

输出:

# A tibble: 9 × 2 
    subj rank 
* <chr> <dbl> 
1  A 1.0 
2  B 2.0 
3  C 4.0 
4  D 4.0 
5  E 4.0 
6  F 6.0 
7  G 7.5 
8  H 7.5 
9  I 9.0 

的另一种方法:

m <- aggregate(rank~subj, data=df, mean) 
m <- apply(m, 1, function(x) data.frame(subj = unlist(strsplit(x[1], ",")), rank = x[2])) 
m <- do.call(rbind, m) 
rownames(m) <- NULL 
m 

输出:

subj rank 
1 A 1.0 
2 B 2.0 
3 C 4.0 
4 D 4.0 
5 E 4.0 
6 F 6.0 
7 G 7.5 
8 H 7.5 
9 I 9.0 
+0

最终的mean,我没有使用这个脚本因为我不活在'tidyverse'中,但是按平均排列顺序的逻辑首先解决了问题。非常感谢。 – gavago

+0

@gavago不客气。我添加了另一种不需要'tidyverse'或'dplyr'的方法。 – kitman0804

2

data.table版本:

#library(data.table) #version 1.9.8 
setDT(df) 
df[, .(subj=unlist(strsplit(subj[1], ",")), rank=mean(rank)), by=subj][,-1] 

# subj rank 
#1: A 1.0 
#2: B 2.0 
#3: C 4.0 
#4: D 4.0 
#5: E 4.0 
#6: F 6.0 
#7: G 7.5 
#8: H 7.5 
#9: I 9.0 
+0

当我在示例脚本上运行'data.table'代码时,输​​出只是简单的'-1'。我对'data.table'不是很熟悉,所以我不确定问题出在哪里。 – gavago

+0

@ user3166232尝试从最后删除'[,-1]'。你可能有不同版本的软件包,导致一些细微的差异 - 我在v1.9.8上。 – thelatemail

2

我的版本与splitstackshapeaggregate。逻辑是一样的,我们用逗号分割字符串并采用subj的意思。

library(splitstackshape) 
aggregate(rank~subj, cSplit(df, "subj", ",", "long"), mean) 

# subj rank 
#1 A 1.0 
#2 B 2.0 
#3 C 4.0 
#4 D 4.0 
#5 E 4.0 
#6 F 6.0 
#7 G 7.5 
#8 H 7.5 
#9 I 9.0 

其中

cSplit(df, "subj", ",", "long") 

#  rank subj 
# 1: 1 A 
# 2: 2 B 
# 3: 3 C 
# 4: 3 D 
# 5: 3 E 
# 6: 4 C 
# 7: 4 D 
# 8: 4 E 
# 9: 5 C 
#10: 5 D 
#11: 5 E 
#12: 6 F 
#13: 7 G 
#14: 7 H 
#15: 8 G 
#16: 8 H 
#17: 9 I 
0

下面是使用tidyverse另一种选择。该数据集是通过使用拆分separate_rows,然后通过“SUBJ”分组的“SUBJ”列转换为“长”格式,得到“排名”

library(tidyverse) 
separate_rows(df, subj) %>% 
     group_by(subj) %>% 
     summarise(rank = mean(rank)) 
# A tibble: 9 × 2 
# subj rank 
# <chr> <dbl> 
#1  A 1.0 
#2  B 2.0 
#3  C 4.0 
#4  D 4.0 
#5  E 4.0 
#6  F 6.0 
#7  G 7.5 
#8  H 7.5 
#9  I 9.0