2013-04-26 2 views
3

Как сделать summarize a data.table with unreliable data через несколько столбцов?Подведите таблицу данных по нескольким столбцам

В частности, учитывая

fields <- c("country","language") 
dt <- data.table(user=c(rep(3, 5), rep(4, 5)), 
       behavior=c(rep(FALSE,5),rep(TRUE,5)), 
       country=c(rep(1,4),rep(2,6)), 
       language=c(rep(6,6),rep(5,4)), 
       event=1:10, key=c("user",fields)) 
dt 
#  user behavior country language event 
# 1: 3 FALSE  1  6  1 
# 2: 3 FALSE  1  6  2 
# 3: 3 FALSE  1  6  3 
# 4: 3 FALSE  1  6  4 
# 5: 3 FALSE  2  6  5 
# 6: 4  TRUE  2  5  7 
# 7: 4  TRUE  2  5  8 
# 8: 4  TRUE  2  5  9 
# 9: 4  TRUE  2  5 10 
# 10: 4  TRUE  2  6  6 

Я хочу, чтобы получить

# user behavior country.name country.support language.name language.support 
# 1: 3 FALSE   1    0.8    6    1.0 
# 2: 4  TRUE   2    1.0    5    0.8 

(здесь х.name является наиболее распространенным х для user и х.support является доля события, где эта вершина x наблюдалось)

без того, чтобы пройти через обе fields вручную, как это:

users <- dt[, sum(behavior) > 0, by=user] # have behavior at least once 
setnames(users, "V1", "behavior") 
dt.out <- dt[, .N, by=list(user,country) 
      ][, list(country[which.max(N)],max(N)/sum(N)), by=user] 
setnames(dt.out, c("V1", "V2"), paste0("country",c(".name", ".support"))) 
users <- users[dt.out] 
dt.out <- dt[, .N, by=list(user,language) 
      ][, list(language[which.max(N)], max(N)/sum(N)), by=user] 
setnames(dt.out, c("V1", "V2"), paste0("language",c(".name", ".support"))) 
users <- users[dt.out] 
users 
# user behavior country.name country.support language.name language.support 
# 1: 3 FALSE   1    0.8    6    1.0 
# 2: 4  TRUE   2    1.0    5    0.8 

Фактическое количество fields составляет 5 и я хочу, чтобы избежать необходимости повторять один и тот же код для каждого поля в отдельности, и нужно отредактировать эту функцию, если я когда-либо изменю fields. Обратите внимание, что этот вопрос является сущностью этого вопроса, подсчет поддержки был любезно объяснен мне elsewhere.

Как и в the referenced question, мой набор данных имеет около 10^7 строк, поэтому мне действительно нужно решение, которое масштабируется; было бы неплохо, если бы я мог избежать ненужного копирования, например, в users <- users[dt.out].

+0

Не могли бы вы объяснить, что такое 'x.support' –

+0

@RicardoSaporta: Я добавил несколько слов о том, что такое' support', но вы должны посмотреть ссылочный вопрос для получения более подробной информации. – sds

+1

Вы можете заменить 'dt [, sum (поведение)> 0, by = user]' на 'dt [, any (behavior), by = user]'. Вы также должны объяснить, почему вы хотите программно обращаться к нескольким столбцам. Либо у вас всего несколько столбцов, то, вероятно, это не стоит беспокоиться. Или у вас много столбцов, тогда было бы лучше изменить формат data.table в формате long. – Roland

ответ

5

Это решение проблемы?

fields <- c("country","language") 
dt <- data.table(user=c(rep(3, 5), rep(4, 5)), 
      behavior=c(rep(FALSE,5),rep(TRUE,5)), 
      country=c(rep(1,4),rep(2,6)), 
      language=c(rep(6,6),rep(5,4)), 
      event=1:10, key=c("user",fields)) 

CalculateSupport <- function(dt, name) { 
    x <- dt[, .N, by = eval(paste0('user,', name))] 
    setnames(x, name, 'name') 
    x <- x[, list(name[which.max(N)], max(N)/sum(N)), by = user] 
    setnames(x, c('V1', 'V2'), paste0(name, c(".name", ".support"))) 
    x 
} 

users <- dt[, sum(behavior) > 0, by=user] 
setnames(users, "V1", "behavior") 

Reduce(function(x, name) x[CalculateSupport(dt, name)], fields, users) 

приводит к

user behavior country.name country.support language.name language.support 
1: 3 FALSE   1    0.8    6    1.0 
2: 4  TRUE   2    1.0    5    0.8 

P.S. Пожалуйста, обратите внимание на комментарий Рикардо. SO полна замечательных людей, которые готовы помочь, но вы должны относиться к ним красиво и с уважением.

+0

спасибо за ответ. Я никогда не собираюсь быть грубым, и я не вижу в моих вопросах ничего грубого – sds

1

Я не могу сделать это в одном выражении, так как я не уверен, как повторно использовать созданное поле в выражении data.table. Это также, вероятно, не самый эффективный способ. Возможно, это послужит хорошей отправной точкой.

#Find most common country and language for each user 
summ.dt<-dt[,list(behavior.summ=sum(behavior)>0, 
    country.name=dt[user==.BY[[1]],.N,by=country][N==max(N),country], 
    language.name=dt[user==.BY[[1]],.N,by=language][N==max(N),language]), 
by=user] 

#Get support for each country and language for each user 
summ.dt[,c("country.support","language.support"):=list(
    nrow(dt[user==.BY[[1]] & country==country.name])/nrow(dt[user==.BY[[1]]]), 
    nrow(dt[user==.BY[[1]] & language==language.name])/nrow(dt[user==.BY[[1]]]) 
),by=user] 

    user behavior.summ country.name language.name country.support language.support 
1: 3   FALSE   1    6    0.8    1.0 
2: 4   TRUE   2    5    1.0    0.8 
+0

вся суть моего вопроса заключается в том, чтобы избежать отдельного выражения для каждого элемента вектора 'fields' – sds

Смежные вопросы