Создайте спецификацию строки col_types для read_csv на основе существующего фрейма данных

У меня есть data.frame или tibble, который в одном скрипте записывается в файл CSV. В другом сценарии тот же файл CSV считывается в data.frame или tibble. Используя read_csv() с параметром col_types=, я могу указать типы столбцов для чтения. Вот пример:

# Create an example dataframe
df <- tibble::tibble(a=1L
                     , b=1.0
                     , c="a"
                     , d=TRUE
                     , e=lubridate::ymd_hms("2019-03-19T13:15:18Z")
                     , f=lubridate::ymd("2019-03-19")
                     , g=factor("a"))

# Write csv to file
readr::write_csv(df, "temp.csv")

# read it back in, supplying a col_types string spec
readr::read_csv("temp.csv", col_types="idclTDf")
#> # A tibble: 1 x 7
#>       a     b c     d     e                   f          g    
#>   <int> <dbl> <chr> <lgl> <dttm>              <date>     <fct>
#> 1     1     1 a     TRUE  2019-03-19 13:15:18 2019-03-19 a

Создано 19 марта 2019 г. пакетом REPEX (v0.2.1)

Проблема в том, что мне нужно знать параметр col_types= функции read_csv() (или дать ему угадать, чего я не хочу делать). Я бы хотел каким-то образом взять исходный df и, прежде чем я его напишу, сгенерировать строку col_types из объекта df, который можно использовать для чтения выгруженного CSV обратно. То есть мне нужно что-то, что создаст строка "idclTDf", заданная в качестве аргумента data.frame.

Я вижу, что для этого есть запрос функции (и я добавил свои два цента) здесь: https://github.com/tidyverse/readr/issues/895.


person mpettis    schedule 19.03.2019    source источник
comment
Это было бы очень полезно и, надеюсь, будет реализовано в пакете readr. Я думаю, что пакет csvy и более общий _ 3_ структура предназначена для устранения необходимости в этой функции, но разработка пакета csvy R приостановлена ​​на неопределенное время, и я не знаю любых альтернатив, которые могут набирать обороты.   -  person bschneidr    schedule 28.03.2019


Ответы (1)


У меня есть решение, и оно работает, но я считаю его очень неполным и не застывшим. Вот моя попытка решения.

# https://github.com/tidyverse/readr/issues/895
# Create function to take a tibble and return a character string that can be used in `readr::read_csv()`
# as the `col_types` argument to re-read this back into a dataframe after it had been written out
# by `write_csv()`.

get_col_types_short <- function(.df) {
    # Get column classes from input dataframe
    lst_col_classes__ <- purrr::map(.df, ~ class(.x))

    # Map classes to known single-character col_types indicator
    vl_col_class_char__ <- purrr::map_chr(lst_col_classes__, function(.e) {
        dplyr::case_when(
              "logical" %in% .e   ~ "l"
            , "integer" %in% .e   ~ "i"
            , "numeric" %in% .e   ~ "d"
            , "double" %in% .e    ~ "d"
            , "character" %in% .e ~ "c"
            , "factor" %in% .e    ~ "f"
            , "Date" %in% .e      ~ "D"
            , "POSIXct" %in% .e   ~ "T"
            , TRUE                ~ "c"
        )
    })

    # Return vector of single-character col_type indicator.
    # Element name is the source column it came from.
    vl_col_class_char__
}

# Test it:
df <- tibble::tibble(a=1L
                     , b=1.0
                     , c="a"
                     , d=TRUE
                     , e=lubridate::ymd_hms("2019-03-19T13:15:18Z")
                     , f=lubridate::ymd("2019-03-19")
                     , g=factor("a"))

v__ <- get_col_types_short(df)

# Show what is actually returned
v__
#>   a   b   c   d   e   f   g 
#> "i" "d" "c" "l" "T" "D" "f"

# Collapse it to show how to use it
paste(v__, collapse="")
#> [1] "idclTDf"


# Write csv to file
readr::write_csv(df, "temp.csv")

# read it back in, using the above col_types string spec
readr::read_csv("temp.csv", col_types=paste(v__, collapse=""))
#> # A tibble: 1 x 7
#>       a     b c     d     e                   f          g    
#>   <int> <dbl> <chr> <lgl> <dttm>              <date>     <fct>
#> 1     1     1 a     TRUE  2019-03-19 13:15:18 2019-03-19 a

Создано 19 марта 2019 г. пакетом REPEX (v0.2.1)

person mpettis    schedule 19.03.2019
comment
Что, по вашему мнению, нужно улучшить? Я думаю это отличная идея - person camille; 20.03.2019
comment
Мы, я не уверен, что я рассмотрел все возможные типы вариантов для date и datetime, у меня нет обработки ошибок, может быть лучше иметь некороткую функцию, которая возвращает цитируемые вызовы, такие как 'col_character ()' и т. Д. Я думаю, что это удобное начало, и я еще не проверял крайние случаи. И я сомневаюсь, что я первый, у кого возникла эта проблема, и может быть найдено лучшее, более надежное решение. - person mpettis; 20.03.2019
comment
Похоже, что это недавно было решено в readr с новой функцией as.col_type(). - person hplieninger; 10.05.2019
comment
@hplieninger, я думаю, это должно быть as.col_spec(), верно? В текущей (1.3.1) версии readr нет as.col_type(). - person D. Woods; 26.09.2019
comment
@ D.Woods, да, вроде функцию переименовали в as.col_spec(). - person hplieninger; 26.09.2019