2016-03-11 2 views
3

Игрушки примера:Чистого способ вычисления вероятности перехода между двумя столбцами в data.table R

library(data.table) 

set.seed(1) 
n_people <- 100 
groups <- c("A", "B", "C") 
example_table <- data.table(person_id=seq_len(n_people), 
          group_2010=sample(groups, n_people, TRUE), 
          group_2011=sample(groups, n_people, TRUE)) 

## Error-prone and requires lots of typing -- programmatic alternative? 
transition_probs <- example_table[, list(pr_A_2011=mean(group_2011=="A"), 
             pr_B_2011=mean(group_2011=="B"), 
             pr_C_2011=mean(group_2011=="C")), 
             by=group_2010] 
transition_probs # Essentially a transition matrix giving Pr[group_2011 | group_2010] 

# group_2010 pr_A_2011 pr_B_2011 pr_C_2011 
# 1:   A 0.1481481 0.5185185 0.3333333 
# 2:   B 0.3684211 0.3947368 0.2368421 
# 3:   C 0.3142857 0.3142857 0.3714286 

«ручной» подход выше хорошо, когда эти группы A, B, C, но получает грязно если есть больше групп (или если у нас есть только вектор groups, но мы не знаем заранее, что он содержит).

Есть ли способ «data.table way» для вычисления объекта transition_probs в моем примере кода выше? Можно ли заменить (pr_A_2011 = ...) на что-то программное?

Я обеспокоен тем, что если я добавлю группу D, мне придется редактировать код в нескольких местах, в частности, набрав pr_D_2011=mean(group_2011=="D").

ответ

3

Я бы

lvls = example_table[, sort(unique(c(group_2010, group_2011))) ] 
x = dcast(example_table, group_2010~group_2011)[, N := Reduce(`+`,.SD), .SDcols=lvls] 

# group_2010 A B C N 
# 1:   A 6 9 15 30 
# 2:   B 15 4 12 31 
# 3:   C 11 11 17 39 

Отсюда, если вы хотите вероятности перехода, просто разделить на N:

x[, (lvls) := lapply(.SD,`/`, x$N), .SDcols=lvls] 
# or, with data.table 1.9.7+ 
x[, (lvls) := lapply(.SD,`/`, N), .SDcols=lvls] 

# group_2010   A   B   C N 
# 1:   A 0.1481481 0.5185185 0.3333333 27 
# 2:   B 0.3684211 0.3947368 0.2368421 38 
# 3:   C 0.3142857 0.3142857 0.3714286 35 
2

Конструкция data.table намеренно предназначается, чтобы быть совместимым с операциями на data.frames, поэтому, если вы не можете (а) доказать, что эта операция является огромным узким местом и (б) продемонстрировать, что альтернативные решения значительно быстрее, почему бы не придерживаться краткости и ясности:

prop.table(table(example_table[,2:3,with=FALSE]),1) 
 
      group_2011 
group_2010   A   B   C 
     A 0.1481481 0.5185185 0.3333333 
     B 0.3684211 0.3947368 0.2368421 
     C 0.3142857 0.3142857 0.3714286 
+0

Спасибо - я действительно не знал о 'prop.table'! – Adrian

1

Я вижу, как текущие ответы очень хорошо адресация ваш вопрос. Затем я отвечу на него более общим образом.
Если вы хотите использовать реальную программную мощность, вы можете использовать функцию языка R.

R относится к классу языков программирования, в которых подпрограммы имеют возможность изменять или строить другие подпрограммы и оценивать результат как неотъемлемую часть самого языка.

library(data.table) 
set.seed(1) 
n_people <- 100 
groups <- c("A", "B", "C") 
example_table <- data.table(person_id=seq_len(n_people), 
          group_2010=sample(groups, n_people, TRUE), 
          group_2011=sample(groups, n_people, TRUE)) 
f = function(data, groups, years) { 
    stopifnot(is.data.table(data), length(groups) > 0L, length(years) == 2L, paste0("group_", years) %in% names(data)) 
    j.names = sprintf("pr_%s_%s", c(groups), years[2L]) 
    j.vals = lapply(setNames(groups, j.names), function(group) call("mean", call("==", as.name(sprintf("group_%s", years[2L])), group))) 
    jj = as.call(c(list(as.name(".")), j.vals)) 
    data[, eval(jj), by = c(sprintf("group_%s", years[1L]))] 
} 
f(example_table, groups, 2010:2011) 
# group_2010 pr_A_2011 pr_B_2011 pr_C_2011 
#1:   A 0.1481481 0.5185185 0.3333333 
#2:   B 0.3684211 0.3947368 0.2368421 
#3:   C 0.3142857 0.3142857 0.3714286 

Нет необходимости замены кода в нескольких местах, просто передать аргументы функции.

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