2017-02-14 31 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

В конце концов, я не использовал этот сценарий потому что я не живу в «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

Моя версия с splitstackshape и aggregate. Логика такая же, мы разделяем строку запятой и принимаем среднее значение на 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. Набор данных преобразуются в формат «длинный» путем разделения столбца «Subj» с помощью separate_rows, затем сгруппированной по «Subj», получить mean от «ранга»

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