2013-03-29 3 views
13

упрощенная версия моего набора данных будет выглядеть следующим образом:Накопительный подсчет уникальных значений в R

depth value 
    1  a 
    1  b 
    2  a 
    2  b 
    2  b 
    3  c 

Я хотел бы сделать новый набор данных, где для каждого значения «глубины», я бы совокупное количество уникальных значений, начиная с вершины. например

depth cumsum 
1  2 
2  2 
3  3 

Любые идеи относительно того, как это сделать? Я относительно новым для R.

ответ

1

Хороший первый шаг должен был бы создать столбец TRUE или FALSE, где он TRUE для первого из каждого значения и FALSE для последующих выступлений этого значения. Это можно легко сделать с помощью duplicated:

mydata$first.appearance = !duplicated(mydata$value) 

Перестройка данные лучше всего сделать с помощью aggregate. В этом случае, говорит сумма по first.appearance столбец в каждой подгруппе depth:

newdata = aggregate(first.appearance ~ depth, data=mydata, FUN=sum) 

Результат будет выглядеть так:

depth first.appearance 
1  1 2 
2  2 0 
3  3 1 

Это еще не кумулятивная сумма, хотя. Для этого вы можете использовать функцию cumsum (а затем избавиться от старого столбца):

newdata$cumsum = cumsum(newdata$first.appearance) 
newdata$first.appearance = NULL 

Подведем итоги:

mydata$first.appearance = !duplicated(mydata$value) 
newdata = aggregate(first.appearance ~ depth, data=mydata, FUN=sum) 
newdata$cumsum = cumsum(newdata$first.appearance) 
newdata$first.appearance = NULL 

Выход:

depth cumsum 
1  1  2 
2  2  2 
3  3  3 
0

Вот еще одно решение используя lapply(). С помощью unique(df$depth) сделайте вектор уникальных значений depth, а затем для каждого такого значения подмножество только те value значения, где depth равно или меньше определенного значения depth. Затем вычислите длину уникальных значений value. Это значение длины сохраняется в cumsum, тогда depth=x даст значение определенного уровня глубины. С do.call(rbind,...) сделать его как один фрейм данных.

do.call(rbind,lapply(unique(df$depth), 
       function(x) 
      data.frame(depth=x,cumsum=length(unique(df$value[df$depth<=x]))))) 
    depth cumsum 
1  1  2 
2  2  2 
3  3  3 
12

Я считаю это идеальный случай использования factor и тщательно установки levels. Я буду использовать data.table здесь с этой идеей. Убедитесь, что ваш столбец value равен character (не является абсолютным требованием).

  • шаг 1: Получите ваш data.frame преобразованы в data.table, принимая только unique строки.

    require(data.table) 
    dt <- as.data.table(unique(df)) 
    setkey(dt, "depth") # just to be sure before factoring "value" 
    
  • шаг 2: Преобразование value к factor и принуждают к numeric. Удостоверьтесь, что установил уровни самостоятельно (это важно).

    dt[, id := as.numeric(factor(value, levels = unique(value)))] 
    
  • шаг 3: Установите ключевой столбец depth для подмножеств и просто выбрать последнее значение

    setkey(dt, "depth", "id") 
    dt.out <- dt[J(unique(depth)), mult="last"][, value := NULL] 
    
    # depth id 
    # 1:  1 2 
    # 2:  2 2 
    # 3:  3 3 
    
  • Шаг 4: Так как все значения в строках с увеличением глубины должны иметь на наименьшее значение значение предыдущей строки, вы должны использовать cummax, чтобы получить окончательный вывод.

    dt.out[, id := cummax(id)] 
    

Edit: Приведенный выше код был в иллюстративных целях. На самом деле вам вообще не нужна третья колонка. Вот как я напишу окончательный код.

require(data.table) 
dt <- as.data.table(unique(df)) 
setkey(dt, "depth") 
dt[, value := as.numeric(factor(value, levels = unique(value)))] 
setkey(dt, "depth", "value") 
dt.out <- dt[J(unique(depth)), mult="last"] 
dt.out[, value := cummax(value)] 

Вот более сложным примером и выход из кода:

df <- structure(list(depth = c(1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 6), 
       value = structure(c(1L, 2L, 3L, 4L, 1L, 3L, 4L, 5L, 6L, 1L, 1L), 
       .Label = c("a", "b", "c", "d", "f", "g"), class = "factor")), 
       .Names = c("depth", "value"), row.names = c(NA, -11L), 
       class = "data.frame") 
# depth value 
# 1:  1  2 
# 2:  2  4 
# 3:  3  4 
# 4:  4  5 
# 5:  5  6 
# 6:  6  6 
+1

Вот 'dplyr' версия:' DF%>% устраивают (глубина)%>% мутировать (значение = cummax (as.numeric (фактор (значение, уровни = уникальный (значение)))))%>% arr (глубина, desc (значение))%>% различная (глубина) '. –

+1

Этот метод обычно применяется, когда значения 'depth' и' value' являются строковыми значениями. Благодаря! – ecoe

+0

@Arun Это отличное решение! Благодаря! – asterx

5

Вот еще одна попытка:

numvals <- cummax(as.numeric(factor(mydf$value))) 
aggregate(numvals, list(depth=mydf$depth), max) 

Что дает:

depth x 
1  1 2 
2  2 2 
3  3 3 

кажется для работы с примером Аруна тоже:

depth x 
1  1 2 
2  2 4 
3  3 4 
4  4 5 
5  5 6 
6  6 6 
+1

Я не совсем уверен, но кажется, что и «глубина», и «значение» должны быть одновременно отсортированы. Например, этот метод не будет считать уникальное появление 'c' независимо от того, как вы' setkey() 'this' data.table': 'mydf = data.table (data.frame (depth = c (1,1) , 2,2,6,7), значение = c («a», «b», «g», «h», «b», «c»))). – ecoe

3

Это может быть написано относительно чистым способом с помощью одного оператора SQL с использованием пакета sqldf. Предположим, DF оригинальный кадр данных:

library(sqldf) 

sqldf("select b.depth, count(distinct a.value) as cumsum 
    from DF a join DF b 
    on a.depth <= b.depth 
    group by b.depth" 
) 
+0

Это очень полезно, если предположить, что 'depth' является числовым. Если 'depth' представляет собой строковое или строковое представление даты, как это было в моем случае, это может быть очень дорогостоящей операцией. – ecoe

+1

Во многих случаях скорость неважна, и ясность является более важной проблемой. Если производительность важна, вам действительно нужно ее протестировать, а не делать предположения, и если ее найти слишком медленно, добавьте индекс и снова проверьте его. –

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