tgoop.com/stats_for_science/84
Last Update:
История факторов в R
Сегодня разберем историю факторов в R, почему раньше при чтении таблиц аргумент stringsAsFactors
по умолчанию был TRUE
, и почему с версии R 4.0 это отменили, а также нужны ли факторы сейчас.
Моя история знакомства с факторами в R была такова: когда я только начинала осваивать R (это было в 2017 году), и использовала его для работы с табличками дифференциальной экспрессии генов, то мне сразу сказали, что при чтении таблицы надо использовать аргумент stringsAsFactors = FALSE
, чтобы строки не превратились в факторы. Прозвучало это супер непонятно, но далее я обратила внимание, что если этого не делать, то после фильтрации таблицы в строковых переменных остаются какие-то уровни (levels), это было неудобно, поэтому я стала следить, чтобы всегда при чтении таблиц было указано stringsAsFactors = FALSE
. И потом при работе уже с дисперсионным анализом поняла, зачем нужны были факторы, и как их использовать, но все равно они оставались чем-то загадочным, средним между строкой и числом, даже казались мне ближе к строке (но это не так, сейчас разберем почему).
Изначально R был создан и использовался в первую очередь для статистических задач, и поэтому превращение всех строк в факторы при чтении таблиц было оправданно, поскольку не числовая переменная в таблице скорее всего означала категории (пол, группа крови, наличие болезни и тп). Для дальнейшего статистического анализа, например, при создании моделей с помощью функций lm()
, glm()
, факторы были нужны для корректной работы этих функций.
Кроме этого, есть еще одна менее очевидная причина. Раньше хранение факторов занимало меньше места в памяти, чем хранение строк. Дело в том, что факторы во внутреннем представлении R – это integer, то есть целые числа, соответственно занимают около 4 байт памяти. При этом строки занимают больше памяти и при работе с категориальными переменными каждая строка таблицы занимала свое место в памяти (то есть если записано disease
в 100 строках, то грубо говоря и памяти будет занято как место занимаемое строкой disease
*100
) [1].
Плюс сравнивать факторы было проще, чем строки, поскольку сравнение чисел происходит за фиксированное время, в то время как для сравнения строк необходимо было "проходить" каждую строку побайтно.
Между прочим, это обновление было сделано благодаря проекту Bioconductor, потому что разработчикам приходилось работать с большими строками, например, с последовательностями нуклеиновых кислот и белков, и без хеширования строк работа со старым типом была максимально неэффективной.
После этого выигрыш в хранении и сравнении строк в виде факторов перестал быть существенным, тем не менее факторы все еще необходимы в lm
, glm
и других подобных функциях, а также для упорядочивания категорий на графике.
Иллюстрация объема памяти, занимаемого на хранение строк (пример взят из книги Advanced R):
banana <- "bananas bananas bananas"Мы создали 100 копий строки, но при этом занимаемый объем увеличился всего в 6.8 раз!
obj_size(banana)
#> 136 B
obj_size(rep(banana, 100))
#> 928 B
При чтении таблиц средствами базового R аргумент
stringsAsFactors
был TRUE
, и только в 2020 году с версии R 4.0 разработчики R это изменили на FALSE
. Только в функциях из пакета readr
строки не превращались в факторы по умолчанию. На мой взгляд, разработчики R довольно поздно изменили дефолтное поведение функций read.table
и read.csv
, но лучше уж поздно чем никогда, хотя я сейчас пользуюсь только readr
-ом для загрузки таблиц (и fread
, при необходимости).Ссылки на источники:
1. https://simplystatistics.org/posts/2015-07-24-stringsasfactors-an-unauthorized-biography/
2. https://notstatschat.rbind.io/2015/07/25/stringsasfactors-sigh/
3. https://notstatschat.rbind.io/2022/03/31/stringsasfactors-do-you-feel-lucky/
#R #history #baseR