Поведение <- NULL в списках по сравнению с data.frames для удаления данных - программирование
Подтвердить что ты не робот

Поведение <- NULL в списках по сравнению с data.frames для удаления данных

Многие пользователи R в конечном итоге определяют множество способов удаления элементов из своих данных. Один из способов - использовать NULL, особенно если вы хотите сделать что-то вроде сброса столбца из data.frame или удалить элемент из list.

В конце концов, пользователь сталкивается с ситуацией, когда они хотят удалить сразу несколько столбцов из data.frame, и они попадают в <- list(NULL) в качестве решения (поскольку использование <- NULL приведет к ошибке).

A data.frame - особый тип list, поэтому было бы не слишком сложно представить, что подходы к удалению элементов из list должны быть такими же, как удаление столбцов из data.frame. Однако они дают разные результаты, как видно из приведенного ниже примера.

## Make some small data--two data.frames and two lists
cars1 <- cars2 <- head(mtcars)[1:4]
cars3 <- cars4 <- as.list(cars2)

## Demonstration that the `list(NULL)` approach works
cars1[c("mpg", "cyl")] <- list(NULL)
cars1
#                   disp  hp
# Mazda RX4          160 110
# Mazda RX4 Wag      160 110
# Datsun 710         108  93
# Hornet 4 Drive     258 110
# Hornet Sportabout  360 175
# Valiant            225 105

## Demonstration that simply using `NULL` does not work
cars2[c("mpg", "cyl")] <- NULL
# Error in `[<-.data.frame`(`*tmp*`, c("mpg", "cyl"), value = NULL) : 
#   replacement has 0 items, need 12

Переключитесь на применение той же концепции до list и сравните разницу в поведении.

## Does not fully drop the items, but sets them to `NULL`
cars3[c("mpg", "cyl")] <- list(NULL)
# $mpg
# NULL
# 
# $cyl
# NULL
# 
# $disp
# [1] 160 160 108 258 360 225
# 
# $hp
# [1] 110 110  93 110 175 105

## *Does* drop the `list` items while this would
##   have produced an error with a `data.frame`
cars4[c("mpg", "cyl")] <- NULL
# $disp
# [1] 160 160 108 258 360 225
# 
# $hp
# [1] 110 110  93 110 175 105

Основные вопросы, которые у меня есть, если a data.frame является list, почему в этом случае он ведет себя по-другому? Есть ли надежный способ узнать, когда элемент будет удален, когда он выдает ошибку, и когда ему будет просто присваивается значение NULL? Или мы зависим от проб и ошибок для этого?

4b9b3361

Ответ 1

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Это относительно длинный ответ, не очень ясный и не очень интересный, поэтому не стесняйтесь пропустить его или просто прочитать (вроде) вывод.

Я пробовал немного отслеживать [<-.data.frame, как было предложено Ари Б. Фридманом. Отладка начинается в строке 162 функции, где есть тест, чтобы определить, является ли value (аргумент замены) не списком.

Случай 1: value не является списком

Тогда он рассматривается как вектор. Матрицы и массивы рассматриваются как один вектор, например, на странице справки:

Обратите внимание, что когда значением замены является массив (включая матрицу) он не рассматривается как ряд столбцов (как "data.frame и 'As.data.frame do), но вставлен как один столбец.

Если в LHS выбран только один столбец кадра данных, то единственным ограничением является то, что количество заменяемых строк должно быть равно или кратно length(value). Если это так, value при необходимости перерабатывается с помощью rep и преобразуется в список. Если length(value)==0, нет никакой утилизации (как это невозможно), а value просто преобразуется в список.

Если в LHS выбрано несколько столбцов кадра данных, то ограничение немного сложнее: length(value) должно быть равно или кратно общему числу элементов, подлежащих замене, то есть количеству строк * количество столбцов.

Точный тест следующий:

(m < n * p && (m == 0L || (n * p)%%m))

Где n - количество строк, p количество столбцов и m длина value. Если условие FALSE, то value преобразуется в матрицу n x p (при необходимости перерабатывается), и матрица разделяется столбцами на список.

Если value равно NULL, тогда условие TRUE равно m==0, и функция остановлена. Обратите внимание, что проблема возникает для каждого value длины 0. Например,

cars1[,c("mpg")] <- numeric(0)

работает, тогда как:

cars1[,c("mpg","disp")] <- numeric(0)

не выполняется так же, как cars1[,c("mpg","disp")] <- NULL

Случай 2: value - это список

Если value - это список, то он используется для одновременного замены нескольких столбцов. Например:

cars1[,c("mpg","disp")] <- list(1,2)

заменит cars1$mpg вектором 1s и cars1$disp с вектором 2s.

Существует своего рода "двойная рециркуляция", которая происходит здесь:

  • во-первых, длина списка value должна быть меньше или равна числу столбцов, подлежащих замене. Если это меньше, классическая утилизация выполняется.
  • второй, для каждого элемента списка value его длина должна быть равна, больше или кратна количеству строк, подлежащих замене. Если это меньше, для каждого элемента списка выполняется другая переработка, чтобы соответствовать количеству строк. Если это больше, отображается предупреждение.

Когда value в RHS list(NULL), ничего действительно не происходит, поскольку рециркуляция невозможна (rep(NULL, 10) всегда NULL). Но код продолжается, и в конце каждого столбца, который нужно заменить, назначается NULL, т.е. удаляется.

Резюме и (вид) заключения

data.frame и list ведут себя по-разному из-за специфического ограничения на кадры данных, где каждый элемент должен иметь одинаковую длину. Удаление нескольких столбцов путем назначения NULL происходит не из-за значения NULL, а потому, что NULL имеет длину 0. Ошибка возникает из теста, который проверяет, является ли длина назначенного значения кратной количество элементов, подлежащих замене (количество строк * количество столбцов).

Обработка случая value=NULL для нескольких столбцов не кажется затруднительным (добавив около четырех строк простого кода), но для этого нужно рассмотреть NULL как частный случай. Я не могу определить, не обрабатывается ли он, потому что это нарушит логику реализации функции или потому, что у меня будут побочные эффекты, которые я не знаю.