R: считать уникальные значения по категориям

У меня есть данные в R, которые выглядят так:

 Cnty   Yr   Plt       Spp  DBH Ht Age
 1  185 1999 20001 Bitternut  8.0 54  47
 2  185 1999 20001 Bitternut  7.2 55  50
 3   31 1999 20001    Pignut  7.4 71  60
 4   31 1999 20001    Pignut 11.4 85 114
 5  189 1999 20001        WO 14.5 80  82
 6  189 1999 20001        WO 12.1 72  79

Я хотел бы знать количество уникальных видов (Spp) в каждом графстве (Cnty). "unique (dfname $Spp)" дает мне общее количество уникальных видов в кадре данных, но я хотел бы это сделать по графству.

Любая помощь приветствуется! Извините за странное форматирование, это мой первый вопрос о SO.

Спасибо.

Ответ 1

Я попытался сделать ваши данные образца немного интереснее. В ваших образцовых данных в настоящее время имеется только одна уникальная "Spp" для "Cnty".

set.seed(1)
mydf <- data.frame(
  Cnty = rep(c("185", "31", "189"), times = c(5, 3, 2)),
  Yr = c(rep(c("1999", "2000"), times = c(3, 2)), 
         "1999", "1999", "2000", "2000", "2000"),
  Plt = "20001",
  Spp = sample(c("Bitternut", "Pignut", "WO"), 10, replace = TRUE),
  DBH = runif(10, 0, 15)
)
mydf
#    Cnty   Yr   Plt       Spp       DBH
# 1   185 1999 20001 Bitternut  3.089619
# 2   185 1999 20001    Pignut  2.648351
# 3   185 1999 20001    Pignut 10.305343
# 4   185 2000 20001        WO  5.761556
# 5   185 2000 20001 Bitternut 11.547621
# 6    31 1999 20001        WO  7.465489
# 7    31 1999 20001        WO 10.764278
# 8    31 2000 20001    Pignut 14.878591
# 9   189 2000 20001    Pignut  5.700528
# 10  189 2000 20001 Bitternut 11.661678

Далее, как было предложено, tapply является хорошим кандидатом здесь. Объедините unique и length, чтобы получить данные, которые вы ищете.

with(mydf, tapply(Spp, Cnty, FUN = function(x) length(unique(x))))
# 185 189  31 
#   3   2   2 
with(mydf, tapply(Spp, list(Cnty, Yr), FUN = function(x) length(unique(x))))
#     1999 2000
# 185    2    2
# 189   NA    2
# 31     1    1

Если вас интересует простая табуляция (а не уникальные значения), вы можете изучить table и ftable:

with(mydf, table(Spp, Cnty))
#            Cnty
# Spp         185 189 31
#   Bitternut   2   1  0
#   Pignut      2   1  1
#   WO          1   0  2
ftable(mydf, row.vars="Spp", col.vars=c("Cnty", "Yr"))
#           Cnty  185       189        31     
#           Yr   1999 2000 1999 2000 1999 2000
# Spp                                         
# Bitternut         1    1    0    1    0    0
# Pignut            2    0    0    1    0    1
# WO                0    1    0    0    2    0

Ответ 2

Как сказал Джастин, совокупность, вероятно, того, чего вы хотите. Если вы вызываете свой фрейм данных foo, то следующее должно дать вам то, что вы хотите, а именно количество индивидуумов на каждого вида, предполагая, что каждая строка с Butternut представляет собой уникальное лицо, принадлежащее к видам butternut. Примечание. Я использовал foo $Age для вычисления длины вектора, т.е. Числа индивидуумов (строк), принадлежащих каждому виду, но вы могли бы использовать foo $Ht или foo $DBH и т.д.

aggregate(foo$Age, by = foo[c('Spp','Cnty')], length)

Приветствия,

Дэнни

Ответ 3

with(mydf, tapply(Spp, list(Cnty, Yr), 
     FUN = function(x) length(unique(x))))

уникальный запрос не работает с большим набором данных. Я имею в виду данные более 1000 тыс. строк.

Ответ 4

Я хотел добавить к тому, что упоминалось в Handcart и Mohair. Для тех из вас, кто хочет получить результаты кода ниже в кадре данных (полезно в студии R)...

with(mydf, table(Spp, Cnty))
#            Cnty
# Spp         185 189 31
#   Bitternut   2   1  0
#   Pignut      2   1  1
#   WO          1   0  2
ftable(mydf, row.vars="Spp", col.vars=c("Cnty", "Yr"))
#           Cnty  185       189        31     
#           Yr   1999 2000 1999 2000 1999 2000
# Spp                                         
# Bitternut         1    1    0    1    0    0
# Pignut            2    0    0    1    0    1
# WO                0    1    0    0    2    0

Вам нужно поставить модификатор as.data.frame.matrix перед вашим кодом так:

as.data.frame.matrix(with(mydf, table(Spp, Cnty)))

Я был очень знаком с R, когда я пришел на этот пост, и мне потребовалось много времени, чтобы понять это, поэтому я подумал, что поделюсь.

Ответ 5

Простое решение с использованием подхода data.table.

library(data.table)

output <- setDT(mydf)[ , .(count=.N) , by = .(Spp,Cnty)]

если вы хотите изменить формат вывода в более удобный формат таблицы:

library(tidyr)

spread(data=a, key =Spp, count)

#   Cnty Bitternut Pignut WO
# 1:  185         2      2  1
# 2:  189         1      1 NA
# 3:   31        NA      1  2

# or perhaps like this:

spread(data=a, key =Cnty, count)

#          Spp 185 189 31
# 1: Bitternut   2   1 NA
# 2:    Pignut   2   1  1
# 3:        WO   1  NA  2

Ответ 6

Теперь мы можем использовать функцию таллинга, чтобы сделать это проще.

tally(group_by(mydf, Spp, Cnty))

        Spp   Cnty     n
     <fctr> <fctr> <int>
1 Bitternut    185     2
2 Bitternut    189     1
3    Pignut    185     2
4    Pignut    189     1
5    Pignut     31     1
6        WO    185     1
7        WO     31     2

Ответ 7

set.seed(1)
mydf <- data.frame(
  Cnty = rep(c("185", "31", "189"), times = c(5, 3, 2)),
  Yr = c(rep(c("1999", "2000"), times = c(3, 2)), 
         "1999", "1999", "2000", "2000", "2000"),
  Plt = "20001",
  Spp = sample(c("Bitternut", "Pignut", "WO"), 10, replace = TRUE),
  DBH = runif(10, 0, 15)
)
mydf

Функция dplyr::count() выглядит как простое решение:

library(dplyr)
count(mydf, Spp, Cnty)
# A tibble: 7 x 3
# Spp       Cnty      n
# <fct>     <fct> <int>
# 1 Bitternut 185       2
# 2 Bitternut 189       1
# 3 Pignut    185       2
# 4 Pignut    189       1
# 5 Pignut    31        1
# 6 WO        185       1
# 7 WO        31        2