Подтвердить что ты не робот

Совокупное количество уникальных значений в R

Упрощенная версия моего набора данных будет выглядеть так:

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

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

depth cumsum
 1      2
 2      2
 3      3

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

4b9b3361

Ответ 1

Я считаю это идеальным примером использования 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)]
    

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

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

Ответ 2

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

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

Кажется, что работает и с примером @Arun:

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

Ответ 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"
)

Ответ 4

Хорошим первым шагом было бы создать столбец 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

Ответ 5

Вот еще одно решение, использующее 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