Странное поведение для data.frames без имен столбцов
Существует непредвиденное поведение для data.frames без имен столбцов. Следующее работает, как ожидалось:
df <- data.frame(a = 1:5, b = 5:9)
df + 1
## a b
## 1 2 6
## 2 3 7
## 3 4 8
но если мы удалим имена столбцов, то поведение странно:
names(df) <- NULL
df + 1
## data frame with 0 columns and 0 rows
То же самое происходит, если имена удаляются с помощью unname
, setNames
. Любые идеи о том, почему это происходит, и это (по какой-то причине) ожидаемое поведение?
Редактирование: Таким образом, документировано, что безымянные data.frame
имеют неподдерживаемые результаты (спасибо @neilfws, @Suren), но меня также интересует, почему это происходит. Я пытаюсь найти фактический код (?), Который делает этот простой пример тормозом.
Ответы
Ответ 1
В документации для data.frame
говорится:
Имена столбцов должны быть не пустыми, а попытки использовать пустые имена будут иметь неподдерживаемые результаты.
Таким образом, ожидается, что результат не может быть желательным, если имена столбцов пустые.
Ответ 2
Я думаю, что в конечном итоге это происходит из-за того, что R рассматривает объект data.frame
как список со специфическими атрибутами:
## A list with no attributes
list_no_attr1 <- list(c(1,2,3), c(3,2,1))
## The attributes and class of the list
attributes(list_no_attr1)
#> NULL
class(list_no_attr1)
#> "list"
Затем мы можем вручную добавить все атрибуты data.frame
без изменения структуры list
:
## Adding the names to the list (not in the attributes)
list2 <- list_no_attr1
attr(list2, "names") <- c("A", "B")
## The attributes and class of the list
attributes(list2)
#> $names
#> [1] "A" "B"
class(list2)
#> "list"
## Adding the "row.names" attributes
list3 <- list2
attr(list3, "row.names") <- c("1", "2", "3")
## The attributes and class of the list
attributes(list3)
#> $names
#> [1] "A" "B"
#> $row.names
#> [1] "1" "2" "3"
class(list3)
#> "list"
Это все еще список. Теперь, когда мы изменим класс объекта на "data.frame"
и затем он будет использовать метод S3 для data.frame
для print
и всех других связанных функций
## Adding a data.frame class attribute
list_data_frame <- list3
attr(list_data_frame, "class") <- "data.frame"
## The attributes and class of the list
attributes(list_data_frame)
#> $names
#> [1] "A" "B"
#> $row.names
#> [1] "1" "2" "3"
#> $class
#> [1] "data.frame"
class(list_data_frame)
#> "data.frame"
Теперь это будет напечатано как правильный data.frame
. Обратите внимание, что он работает точно так же, как и вокруг, и может преобразовать data.frame
обратно в list
если мы удалим атрибут класса.
## The dataframe
data_frame <- data.frame("A" = c(1,2,3), "B" = c(3,2,1))
## The attributes and class of the list
attributes(data_frame)
#> $names
#> [1] "A" "B"
#> $row.names
#> [1] "1" "2" "3"
#> $class
#> [1] "data.frame"
class(data_frame)
#> "data.frame"
## "Converting" into a list
attr(data_frame, "class") <- NULL
attributes(data_frame)
#> $names
#> [1] "A" "B"
#> $row.names
#> [1] "1" "2" "3"
class(data_frame)
#> "list"
Конечно, он работает только в том случае, если элементы в списке имеют одинаковую длину:
## Creating an unequal list with data.frame attributes
wrong_list <- list(c(1,2,3), c(3,2,1,0))
attr(wrong_list, "names") <- c("A", "B")
attr(wrong_list, "row.names") <- c("1", "2", "3")
attr(wrong_list, "class") <- "data.frame"
wrong_list
#> A B
#> 1 1 3
#> 2 2 2
#> 3 3 1
#> Warning message:
#> In format.data.frame(x, digits = digits, na.encode = FALSE) :
#> corrupt data frame: columns will be truncated or padded with NAs
И это также row.names
ошибкам при отсутствии names
и атрибутов row.names
как указано в других комментариях и ответах на этот вопрос:
## A list coerced into a data.frame without the right attributes
wrong_list <- list(c(1,2,3), c(3,2,1))
attr(wrong_list, "class") <- "data.frame"
wrong_list
#> NULL
#> <0 rows> (or 0-length row.names)