Как узнать количество уровней фактора в тибле? - PullRequest
1 голос
/ 07 августа 2020

Это кажется довольно примитивным c, но количество глаголов в тидиверсе сейчас огромно, и я не знаю, какой пакет искать.

Вот в чем проблема. У меня есть таблица

df <- tibble(f1 = factor(rep(letters[1:3],5)),
             c1 = rnorm(15))

Теперь, если я использую оператор $, я могу легко узнать, сколько уровней находится в множителе.

nlevels(df$f1)
# [1] 3

Но если я использую [] возвращает неверное количество уровней.

nlevels(df[,"f1"])
# [1] 0

Теперь, если df - это data.frame, а не tibble, функция nlevels() работает как с оператором $, так и с * Оператор 1018 *.

Итак, знает ли кто-нибудь тидиверсный эквивалент nlevels(), который работает как с data.frames, так и с таблицами?

Ответы [ 3 ]

3 голосов
/ 07 августа 2020

Разрабатывая ответ от timcdlucas (и комментарии от r2evans ), проблема здесь заключается в поведении различных форм оператора извлечения, а не в поведении tibble . Зачем? a tibble на самом деле является разновидностью data.frame, как показано, когда мы используем функцию str() в таблице.

> library(dplyr)
> aTibble <- tibble(f1 = factor(rep(letters[1:3],5)),
+              c1 = rnorm(15))
> 
> # illustrate that aTibble is actually a type of data frame
> str(aTibble)
tibble [15 × 2] (S3: tbl_df/tbl/data.frame)
 $ f1: Factor w/ 3 levels "a","b","c": 1 2 3 1 2 3 1 2 3 1 ...
 $ c1: num [1:15] -0.5829 0.3682 1.1854 -0.6309 -0.0268 ...

В R есть четыре формы оператора извлечения: [, [[, $ и @; как указано в Что означает знак доллара $ в функции R? .

Первая форма, [, может использоваться для извлечения векторов формы содержимого, списков, матриц или фреймы данных. При использовании с фреймом данных (или тибблом в тидиверсе) он возвращает объект типа data.frame или tibble, если не включен аргумент drop = TRUE, как указано в комментариях к вопросу r2evans.

Поскольку значение по умолчанию drop= в функции [ - FALSE, отсюда следует, что df[,"f1"] дает неожиданный или «неправильный» результат для кода, отправленного с исходным вопросом.

library(dplyr)
aTibble <- tibble(f1 = factor(rep(letters[1:3],5)),
             c1 = rnorm(15))

# produces unexpected answer
nlevels(aTibble[,"f1"])

> nlevels(aTibble[,"f1"])
[1] 0

Аргумент drop = используется при извлечении из матриц или массивов (т. Е. Любого объекта, имеющего атрибут dim, как описано в справке для функции drop () .

> dim(aTibble)
[1] 15  2
> 

Когда мы устанавливаем drop = TRUE, функция извлечения возвращает объект самого низкого доступного типа, то есть все экстенты длиной 1. удаляются. В случае исходного вопроса drop = TRUE с оператором извлечения возвращает коэффициент , который является правильным типом ввода для nlevels().

> nlevels(aTibble[,"f1",drop=TRUE])
[1] 3

Формы [[ и $ оператора извлечения извлекают один объект, поэтому они возвращают объекты типа factor, r требуемый ввод в nlevels().

> str(aTibble$f1)
 Factor w/ 3 levels "a","b","c": 1 2 3 1 2 3 1 2 3 1 ...
> nlevels(aTibble$f1)
[1] 3
> 
> # produces expected answer
> str(aTibble[["f1"]])
 Factor w/ 3 levels "a","b","c": 1 2 3 1 2 3 1 2 3 1 ...
> nlevels(aTibble[["f1"]])
[1] 3
> 

Четвертая форма оператора извлечения, @ (известная как оператор слота), используется с формально определенными объектами, созданными с помощью объектной системы S4, и является не имеет отношения к этому вопросу.

Заключение: Base R все еще актуален при использовании Tidyverse

Per tidyverse.org , tidyverse представляет собой набор пакетов R, которые разделяют основную философию, грамматику и структуры данных. Когда кто-то знакомится с семейством пакетов tidyverse, можно делать много вещей в R, не понимая основ работы Base R.

Тем не менее, когда кто-то включает функции Base R или функции из пакетов за пределами tidyverse в код в стиле tidyverse, важно знать ключевые концепции Base R.

3 голосов
/ 07 августа 2020

Я думаю, вам может потребоваться использовать [[ вместо [, например,

> nlevels(df[["f1"]])
[1] 3
2 голосов
/ 07 августа 2020

df[,"f1"] возвращает тиббл с одним столбцом. Итак, вы делаете nlevels на всем тибле, что не имеет смысла.

df %>% pull('f1') %>% nlevels

дает вам то, что вы хотите.

...