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

Я загрузил исторические данные о погоде Германии, которые в общей сложности хранятся в 1080 txt файле (исходные данные можно найти на этом ftp: Исторические данные о погоде в Германии), где данные каждой отдельной метеостанции хранятся в соответствующем отдельном файле формата txt.

Однако в моем исследовательском случае мне нужно только хранить запись данных о погоде между 01-01-1980 ~ 31-12-2014 в каждом файле, где каждый отдельный текстовый файл содержит данные о погоде, которые превышают указанный выше интервал дат (данные о погоде около 100 лет (не непрерывно)). Будет довольно неэффективно и тяжело работать, если я буду редактировать каждый txt файл вручную и хранить данные о погоде только с 01-01-1980 по 31-12-2014. Возможно, есть способ редактировать каждый текстовый файл программно, в котором сохраняются только записи данных о погоде, которые соответствуют указанному мной диапазону дат, в то время как остальная часть записи данных должна быть удалена, а файл может быть сохранен с исходным форматом и именем.

Я загрузил все записи данных в формате .txt и загрузил их в свой сеанс R. Я умею их читать. Но программно редактировать и хранить только те записи о погоде, которые мне интересны, мне неизвестны. Как я могу сделать это легко в R? Можно ли это сделать в R?

Вот как выглядят данные:

> head(ClmData_files)
[1] "stella/data/germany_histData/produkt_klima_monat_17190101_20161231_00403.txt"
[2] "stella/data/germany_histData/produkt_klima_monat_17570301_19611130_01425.txt"
[3] "stella/data/germany_histData/produkt_klima_monat_17810101_20161231_02290.txt"
[4] "stella/data/germany_histData/produkt_klima_monat_17880101_20161231_05099.txt"
[5] "stella/data/germany_histData/produkt_klima_monat_17920101_19840731_04927.txt"
[6] "stella/data/germany_histData/produkt_klima_monat_18010101_19531231_03382.txt"
> tail(ClmData_files)
[1] "stella/data/germany_histData/produkt_klima_monat_20110901_20161231_00161.txt"
[2] "stella/data/germany_histData/produkt_klima_monat_20131101_20161231_15207.txt"
[3] "stella/data/germany_histData/produkt_klima_monat_20140901_20161231_15444.txt"
[4] "stella/data/germany_histData/produkt_klima_monat_20150801_20161231_01246.txt"
[5] "stella/data/germany_histData/produkt_klima_monat_20160501_20161231_15555.txt"
[6] "stella/data/germany_histData/produkt_klima_monat_20160901_20161231_01886.txt"
> length(ClmData_files)
[1] 1080

Вот как каждый отдельный текстовый файл выглядит в Notepad++ (только первые 10 строк):

STATIONS_ID;MESS_DATUM_BEGINN;MESS_DATUM_ENDE;QN_4;MO_N;MO_TT;MO_TX;MO_TN;MO_FK;MX_TX;MX_FX;MX_TN;MO_SD_S;QN_6;MO_RR;MX_RS;eor
        403;17190101;17190131;    5;  -999;   2.8;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190201;17190228;    5;  -999;   1.1;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190301;17190331;    5;  -999;   5.2;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190401;17190430;    5;  -999;   9.0;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190501;17190531;    5;  -999;  15.1;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190601;17190630;    5;  -999;  19.0;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190701;17190731;    5;  -999;  21.4;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190801;17190831;    5;  -999;  18.8;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190901;17190930;    5;  -999;  13.9;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17191001;17191031;    5;  -999;   9.0;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor

После того, как я загрузил все данные о погоде с указанного выше ftp-сервера на свой локальный диск и прочитал их в R внизу:

lapply(ClmData_files, function(x) {
  read.table(x,
             sep="\t",
             fill=FALSE,
             strip.white=TRUE)
})

Потому что каждый txt файл содержит данные о погоде за почти 100 лет, но меня интересуют только последние 35 лет, чтобы изучить будущую тенденцию изменения климата с помощью регрессионной модели. Теперь мне нужно получить программный доступ к каждому текстовому файлу, а также отредактировать и отфильтровать записи данных о погоде на основе настроенного интервала дат и сохранить их в текущем сеансе R. Есть ли способ сделать это в динамическом программировании на R? Есть предположения?

Обновление:

нам нужно только работать с MESS_DATUM_BEGINN (begin date);MESS_DATUM_ENDE (end date) столбцами в каждом файле и сохранять только записи данных о погоде, которые попадают в интервал дат 1980-01-01 ~ 2014-12-31, и сохранять их в формате csv, такая операция должна быть применена и распространена на все txt файл (всего 1080 файлов). Как я могу сделать это программно в R? Любая идея? Спасибо

Обновление 2:

Теперь я могу загрузить все исторические данные о погоде в Германии с помощью пакета rdwd, вот код, который собирает все данные в сеансе R:

install.packages("rdwd")
library(rdwd)
ftpURL <- selectDWD(name = "", exactmatch = TRUE, 
                    res="monthly", 
                    var="kl", per="historical", current = TRUE)
ftpFile <- dataDWD(file = ftpURL, dir = "stella/input/",sleep = 0)
rowData <- readDWD(ftpFile, fread = FALSE)

Теперь соответствующие исторические данные о погоде доступны на лету: исторические данные о погоде в Германии


person Jared    schedule 17.03.2018    source источник
comment
Начните с проверки того, что вы можете правильно загрузить один текстовый файл. Не думаю, что вы получите то, что хотите, с этим звонком.   -  person De Novo    schedule 18.03.2018
comment
Вы разделяете вкладку, но ваши данные разделяются точкой с запятой   -  person De Novo    schedule 18.03.2018
comment
Это работа по получению и очистке данных. Да, это выполнимо, и есть стратегии, позволяющие сделать это менее болезненным, но они требуют тщательного планирования, и единого подхода нет. Обычно это требует внимания, выявления закономерностей и использования mapply. Внимательно прочтите документацию по read.table для начала.   -  person De Novo    schedule 18.03.2018
comment
r4ds.had.co.nz   -  person Tung    schedule 18.03.2018
comment
Начните работать над этим, и когда у вас возникнет конкретный вопрос о вашем коде (почему он не выполняет то, что вы хотели бы), задайте его здесь :) Однако начните с внимательного прочтения документации для read.table.   -  person De Novo    schedule 18.03.2018
comment
это не сервис написания кода   -  person rawr    schedule 18.03.2018
comment
Вы также можете заметить, что диапазон закодирован в имени файла hte. Поэтому ftp://ftp-cdc.dwd.de/pub/CDC/observations_germany/climate/monthly/kl/historical/monatswerte_KL_00061_19750701_19780831_hist.zip не подходит для ваших целей.   -  person IRTFM    schedule 18.03.2018
comment
@ 42- Спасибо за ваш комментарий. Я заметил, что пакет rdwd может загружать все исторические данные о погоде. Есть какие-нибудь обновления, чтобы сделать это в R? Спасибо   -  person Jared    schedule 18.03.2018


Ответы (1)


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

Итак, давайте сначала прочитаем только один файл. Обратите внимание, что я использую text='...', тогда как вы должны использовать file='...' ... это просто сокращение для воспроизводимых ответов SO.

x <- read.table(text = 'STATIONS_ID;MESS_DATUM_BEGINN;MESS_DATUM_ENDE;QN_4;MO_N;MO_TT;MO_TX;MO_TN;MO_FK;MX_TX;MX_FX;MX_TN;MO_SD_S;QN_6;MO_RR;MX_RS;eor
        403;17190101;17190131;    5;  -999;   2.8;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190201;17190228;    5;  -999;   1.1;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190301;17190331;    5;  -999;   5.2;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190401;17190430;    5;  -999;   9.0;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190501;17190531;    5;  -999;  15.1;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190601;17190630;    5;  -999;  19.0;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190701;17190731;    5;  -999;  21.4;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190801;17190831;    5;  -999;  18.8;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17190901;17190930;    5;  -999;  13.9;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor
        403;17191001;17191031;    5;  -999;   9.0;   -999;   -999;-999;-999;-999;-999;-999;-999;-999;-999;eor',
        sep = ';', header = TRUE)
str(x)
# 'data.frame': 10 obs. of  17 variables:
#  $ STATIONS_ID      : int  403 403 403 403 403 403 403 403 403 403
#  $ MESS_DATUM_BEGINN: int  17190101 17190201 17190301 17190401 17190501 17190601 17190701 17190801 17190901 17191001
#  $ MESS_DATUM_ENDE  : int  17190131 17190228 17190331 17190430 17190531 17190630 17190731 17190831 17190930 17191031
#  $ QN_4             : int  5 5 5 5 5 5 5 5 5 5
#  $ MO_N             : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MO_TT            : num  2.8 1.1 5.2 9 15.1 19 21.4 18.8 13.9 9
#  $ MO_TX            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MO_TN            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MO_FK            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MX_TX            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MX_FX            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MX_TN            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MO_SD_S          : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ QN_6             : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MO_RR            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ MX_RS            : int  -999 -999 -999 -999 -999 -999 -999 -999 -999 -999
#  $ eor              : Factor w/ 1 level "eor": 1 1 1 1 1 1 1 1 1 1

Затем мы можем либо разобрать все даты на классические объекты R Date, либо работать с ними напрямую как с целыми числами, которыми они являются сейчас; сравнение, вероятно, будет работать в любом случае, но для ясности кода (и поскольку я подозреваю, что вы можете провести дальнейший анализ / визуализацию с фактической меткой, подобной дате), я собираюсь сделать дополнительный шаг по преобразованию в правильные объекты даты:

my_ymd <- function(a) as.Date(as.character(a), format='%Y%m%d')
x[c('MESS_DATUM_BEGINN','MESS_DATUM_ENDE')] <- lapply(x[c('MESS_DATUM_BEGINN','MESS_DATUM_ENDE')], my_ymd)
str(x[c('MESS_DATUM_BEGINN','MESS_DATUM_ENDE')])
# 'data.frame': 10 obs. of  2 variables:
#  $ MESS_DATUM_BEGINN: Date, format: "1719-01-01" "1719-02-01" "1719-03-01" "1719-04-01" ...
#  $ MESS_DATUM_ENDE  : Date, format: "1719-01-31" "1719-02-28" "1719-03-31" "1719-04-30" ...

(Есть несколько пакетов, которые делают это быстро и с большей надежностью. Не стесняйтесь использовать любой из них, я предлагаю простой подход base-R с учетом того, что я вижу.)

Отсюда фильтрация диапазона дат, который вы хотите, очень проста (обратите внимание, что я использую разные даты, так как ваш пример не включает даты, которые вы хотите фильтровать):

keep_ymd <- my_ymd(c("17190401", "17190701"))
keep_ymd
# [1] "1719-04-01" "1719-07-01"
x[keep_ymd[1] <= x$MESS_DATUM_BEGINN & x$MESS_DATUM_ENDE <= keep_ymd[2],,drop=FALSE]
#   STATIONS_ID MESS_DATUM_BEGINN MESS_DATUM_ENDE QN_4 MO_N MO_TT MO_TX MO_TN MO_FK MX_TX MX_FX MX_TN MO_SD_S QN_6 MO_RR MX_RS eor
# 4         403        1719-04-01      1719-04-30    5 -999   9.0  -999  -999  -999  -999  -999  -999    -999 -999  -999  -999 eor
# 5         403        1719-05-01      1719-05-31    5 -999  15.1  -999  -999  -999  -999  -999  -999    -999 -999  -999  -999 eor
# 6         403        1719-06-01      1719-06-30    5 -999  19.0  -999  -999  -999  -999  -999  -999    -999 -999  -999  -999 eor

Итак, чтобы объединить это с вашим исходным кодом с использованием lapply, я бы, вероятно, сделал что-то вроде:

rawdata <- lapply(ClmData_files, read.table, sep=';', header=TRUE)
filtered <- lapply(rawdata, function(x) {
  x[c('MESS_DATUM_BEGINN','MESS_DATUM_ENDE')] <- lapply(x[c('MESS_DATUM_BEGINN','MESS_DATUM_ENDE')], my_ymd)
  x[keep_ymd[1] <= x$MESS_DATUM_BEGINN & x$MESS_DATUM_ENDE <= keep_ymd[2],,drop=FALSE]
})

(Я обычно загружаю необработанные данные и храню их, пока не буду уверен, что мои первые несколько шагов верны.)

Изменить

Я думаю (непроверенный) следующий dplyr (и друзья) конвейер может работать:

library(dplyr)
library(tidyr)
library(purrr)

data_frame(fname = ClmData_files) %>%
  mutate(data = map(fname, ~ read.table(., sep=':', header=TRUE))) %>%
  mutate_at(vars(MESS_DATUM_BEGINN, MESS_DATUM_ENDE), funs(my_ymd)) %>%
  unnest() %>%
  filter(
    between(MESS_DATUM_BEGINN, keep_ymd[1], keep_ymd[2]),
    between(MESS_DATUM_ENDE, keep_ymd[1], keep_ymd[2])
  )
person r2evans    schedule 17.03.2018