R преобразовать между объектом зоопарка и фреймом данных, результаты несовместимы для разного количества столбцов?

Мне трудно переключаться между фреймами данных и объектами зоопарка, особенно с сохранением значимых имен столбцов и несогласованностью между одномерными и многомерными случаями:

library(zoo)

#sample data, two species counts over time
t = as.Date(c("2012-01-01", "2012-01-02", "2012-01-03", "2012-01-04"))
n1 = c(4, 5, 9, 7)  #counts of Lepisma saccharina
n2 = c(2, 6, 0, 11) #counts of Thermobia domestica
df = data.frame(t, n1, n2)
colnames(df) <- c("Date", "Lepisma saccharina", "Thermobia domestica")

#converting to zoo loses column names in univariate case...
> z1 <- read.zoo(df[,1:2]) #time series for L. saccharina
> colnames(z1)
NULL
> colnames(z1) <- c("Lepisma saccharina") #can't even set column name manually
Error in `colnames<-`(`*tmp*`, value = "Lepisma saccharina") : 
  attempt to set colnames on object with less than two dimensions
#... but not in multivariate case
> z2 <- read.zoo(df) #time series for both species
> colnames(z2)
[1] "Lepisma saccharina"  "Thermobia domestica"

Чтобы вернуться от объекта зоопарка к фрейму данных в исходном формате, недостаточно использовать as.data.frame, поскольку он не будет включать столбец «Дата» (даты попадают в названия строк): требуется дополнительная работа.

zooToDf <- function(z) {
    df <- as.data.frame(z) 
    df$Date <- time(z) #create a Date column
    rownames(df) <- NULL #so row names not filled with dates
    df <- df[,c(ncol(df), 1:(ncol(df)-1))] #reorder columns so Date first
    return(df)
}

Это отлично работает в многомерном случае, но явно не может восстановить значимое имя столбца в одномерном случае:

> df2b <- zooToDf(z2)
> df2b
        Date Lepisma saccharina Thermobia domestica
1 2012-01-01                  4                   2
2 2012-01-02                  5                   6
3 2012-01-03                  9                   0
4 2012-01-04                  7                  11

> df1b <- zooToDf(z1)
> df1b
        Date z
1 2012-01-01 4
2 2012-01-02 5
3 2012-01-03 9
4 2012-01-04 7

Есть ли простой способ справиться с обоими одномерными и многомерными случаями? Кажется, z1 нужно как-то запомнить имя столбца.


person Silverfish    schedule 28.12.2012    source источник
comment
Примечание для себя: основная проблема отбрасывания, иногда вызывающая несоответствие между случаями с одной и несколькими переменными, часто возникает с фреймами данных, а не только с преобразованием в объекты зоопарка. См. Ответы на часто задаваемые вопросы для data.table datatable.r-forge.r-project.org /datatable-faq.pdf, где разработчики отмечают. В [.data.frame мы очень часто устанавливаем drop=FALSE. Когда мы забываем, ошибки могут возникнуть в крайних случаях, когда выбираются отдельные столбцы, и внезапно возвращается вектор, а не отдельный столбец data.frame. В [.data.table мы воспользовались возможностью, чтобы сделать его последовательным и упасть.   -  person Silverfish    schedule 01.11.2013
comment
В вопросе о непоследовательности говорится о том, как R работает даже без зоопарка. Фактически, зоопарк согласуется с тем, как работает R. Если бы это не сработало, это было бы непоследовательно.   -  person G. Grothendieck    schedule 12.06.2019


Ответы (5)


Для преобразования из фрейма данных в зоопарк используйте read.zoo:

library(zoo)
z <- read.zoo(df)

Также обратите внимание на наличие drop и других аргументов в ?read.zoo.

и для преобразования из зоопарка во фрейм данных, включая индекс, используйте fortify.zoo:

fortify.zoo(z, name = "Date")

(Если загружен ggplot2, вы можете просто использовать fortify.)

Как упоминалось в комментариях под вопросом, этот вопрос, а также некоторые другие ответы либо устарели, либо содержат существенные недопонимания. Предлагаем вам просмотреть https://cran.r-project.org/web/packages/zoo/vignettes/zoo-design.pdf, в котором обсуждается философия дизайна zoo, которая включает согласованность с самим R. Конечно, zoo было бы намного сложнее использовать, если бы вам пришлось запомнить один набор значений по умолчанию для R, а другой - для zoo.

person G. Grothendieck    schedule 12.06.2019

Если вы не хотите сбрасывать размеры, используйте drop=FALSE:

R> (z1 <- read.zoo(df[,1:2], drop=FALSE))
           Lepisma saccharina
2012-01-01                  4
2012-01-02                  5
2012-01-03                  9
2012-01-04                  7

Вы можете сделать что-то вроде write.zoo, если хотите включить индекс зоопарка в качестве столбца в свой data.frame:

zoo.to.data.frame <- function(x, index.name="Date") {
  stopifnot(is.zoo(x))
  xn <- if(is.null(dim(x))) deparse(substitute(x)) else colnames(x)
  setNames(data.frame(index(x), x, row.names=NULL), c(index.name,xn))
}

ОБНОВИТЬ:

Попробовав отредактировать ваш вопрос для краткости, я придумал простой способ создать df2b в соответствии с вашими требованиями (это также будет работать для z1, если вы не опускаете размеры):

R> (df2b <- data.frame(Date=time(z2), z2, check.names=FALSE, row.names=NULL))
        Date Lepisma saccharina Thermobia domestica
1 2012-01-01                  4                   2
2 2012-01-02                  5                   6
3 2012-01-03                  9                   0
4 2012-01-04                  7                  11
person Joshua Ulrich    schedule 28.12.2012
comment
fortify.zoo (z1)? - person Henk; 15.03.2016

Есть более новое простое решение с использованием пакета timetk. Он преобразует несколько форматов временных рядов, включая xts и zoo, в tibbles. Просто оберните as.data.frame, чтобы получить фрейм данных.

timetk::tk_tbl(zoo::read.zoo(df))
# A tibble: 4 x 3
  index      `Lepisma saccharina` `Thermobia domestica`
  <date>                    <dbl>                 <dbl>
1 2012-01-01                    4                     2
2 2012-01-02                    5                     6
3 2012-01-03                    9                     0
4 2012-01-04                    7                    11
person hmhensen    schedule 11.06.2019

Я бы немного обошел. Сначала запишите зоопарк в файл csv, а затем снова прочитайте его в data.frame. Столбец индекса по умолчанию будет называться «Индекс», но вы можете изменить его с помощью параметра.

library(zoo)
date <-
  seq.Date(
    from = as.Date("2017-01-01"),
    to = as.Date("2017-01-10"),
    by = "days"
  )
value <- seq.int(from = 100, to = length(date))
vzoo <- zoo(value, date)
write.zoo(
  vzoo,
  index.name = "Date",
  file = "tmp.txt",
  sep = ",",
  col.names = TRUE
)
vzoo.df <- read.csv("tmp.txt", sep = ',')
person Dinh Quang Duong    schedule 19.01.2017

Вы можете просто создать новый набор данных и добавить as.data.frame, чтобы обернуть fortify.zoo. Это должно помочь. z2 = as.data.frame (fortify.zoo (z, name = "Date"))

person Ami    schedule 05.07.2019