Формирование столбца в csvs при их импорте - PullRequest
0 голосов
/ 19 июня 2020

Я импортирую несколько CSV-файлов шириной в два столбца (они выводятся программой) - первый столбец - это длина волны, а второй - поглощение, но я называю его по имени файла, который нужно объединить. позже, как из этого старого ответа на переполнение стека ( Объединение файлов csv в R в разные столбцы ). Входящие .csvs не имеют заголовков, и я знаю, что то, как я их называю, обрезает первые точки данных. Я бы хотел, чтобы первый столбец не имел десятичных знаков и стандартизировал все числа до четырех цифр - код, который я добавил, работает сам по себе, но не в этом блоке - и я бы предпочел выполнить это форматирование все в одном go. Я сталкиваюсь с ошибками из-за того, что $ неправильный оператор, но когда я использую [], я тоже получаю ошибки об этом. Столбец, с которым мне нужно это сделать, является первым и называется «Длина волны», что также дает мне ошибки, потому что длина волны не существует или не имеет числа c. Есть идеи?

Вот как сейчас выглядит мой скрипт:

for (file in file_list) {
  f <- sub("(.*)\\.CSV", "\\1", file)
  assign(f, read.csv(file = file))
  assign(f, setNames(get(f), c(names(get(f))[0:0], "Wavelength")))
  assign(f, setNames(get(f), c(names(get(f))[1:1], file)))
  floor(f[Wavelength]) #the issues are here
  sprintf("%04d", f$Wavelength) #and here
}

Данные в CSV выглядят так до обработки:

1       401.7664      0.1379457
2       403.8058      0.1390427
3       405.8452      0.1421666
4       407.8847      0.1463629
5       409.9241      0.1477264

I хотел бы, чтобы результат был:

     Wavelength  (file name)
1       0401      0.1379457
2       0403      0.1390427
3       0405      0.1421666
4       0407      0.1463629
5       0409      0.1477264

А вот dput, который запрашивал r2evans:

structure(list(X3.997270e.002 = c(401.7664, 403.8058, 405.8452, 
407.8847, 409.9241, 411.9635), X1.393858e.001 = c(0.1379457, 
0.1390427, 0.1421666, 0.1463629, 0.1477264, 0.1476971)), row.names = 
c(NA, 
6L), class = "data.frame")

Заранее спасибо!

6/24 Обновление: Когда я назначаю имя столбца «Длина волны», оно добавляется только как символ, а не как настоящее имя столбца? Когда я отправляю / возглавляю файлы после того, как они go прошли (без функций sprintf / floor), отображается только имя файла (второй столбец). Когда я открываю csvs в R studio, первый столбец правильно помечен - и даже дальше я могу объединить все csvs, отсортированные по "длине волны":

list_csvs <- mget(sub("(.*)\\.CSV", "\\1", file_list))
all_csvs <- Reduce(function(x, y) merge(x, y, all=T, 
      by=c("Wavelength")), list_csvs, accumulate=F)

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

Я обновил код, чтобы использовать имена столбцов вне read.csv:

for (file in file_list) {
  f <- sub("(.*)\\.CSV", "\\1", file)
  assign(f, read.csv(file = file, 
                     header = FALSE, 
                     row.names = NULL))
  colnames(f) <- c("Wavelength", file)
  print(summary(f))
  print(names(f))
  #floor("Wavelength")   #I'm omitting this to see the console errors
  #sprintf("%04.0f", f["Wavelength"])    #omitting this too
  }

, но я получаю следующую ошибку:

attempt to set 'colnames' on an object with less than two dimensions

Без бита именования и без sprintf / floor я получаю это обратно из сводки и запроса имен для каждого файла:

 Length     Class      Mode 
    1 character character 
NULL

Когда я пытаюсь вызвать первый столбец с помощью f [1], f [[1]], f [, 1] или f [[, 1]], я получаю сообщения об ошибках: неправильное количество размеров ». В среде R я ясно вижу, что каждый фрейм данных имеет длину 2. Я также дважды проверил с помощью .row_names_info(f), что первый столбец не читается как имена строк. Что я делаю не так?

1 Ответ

0 голосов
/ 25 июня 2020

Я собираюсь предложить для этого канал dplyr / tidyr.

Во-первых, настройка данных:

writeLines(
"401.7664,0.1379457
403.8058,0.1390427
405.8452,0.1421666
407.8847,0.1463629
409.9241,0.1477264", "sample1.csv")
file.copy("sample1.csv", "sample2.csv")
file_list <- normalizePath(list.files(pattern = ".*\\.csv$", full.names = TRUE), winslash = "/")
file_list
# [1] "C:/Users/r2/StackOverflow/13765634/sample1.csv"
# [2] "C:/Users/r2/StackOverflow/13765634/sample2.csv"

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

library(dplyr)
library(purrr) # map*
library(tidyr) # pivot_wider
file_list %>%
  set_names(.) %>%
  # set_names(tools::file_path_sans_ext(basename(.))) %>%
  map_dfr(~ read.csv(.x, header = FALSE, col.names = c("freq", "val")),
          .id = "filename") %>%
  mutate(freq = sprintf("%04.0f", freq))
#                                          filename freq       val
# 1  C:/Users/r2/StackOverflow/13765634/sample1.csv 0402 0.1379457
# 2  C:/Users/r2/StackOverflow/13765634/sample1.csv 0404 0.1390427
# 3  C:/Users/r2/StackOverflow/13765634/sample1.csv 0406 0.1421666
# 4  C:/Users/r2/StackOverflow/13765634/sample1.csv 0408 0.1463629
# 5  C:/Users/r2/StackOverflow/13765634/sample1.csv 0410 0.1477264
# 6  C:/Users/r2/StackOverflow/13765634/sample2.csv 0402 0.1379457
# 7  C:/Users/r2/StackOverflow/13765634/sample2.csv 0404 0.1390427
# 8  C:/Users/r2/StackOverflow/13765634/sample2.csv 0406 0.1421666
# 9  C:/Users/r2/StackOverflow/13765634/sample2.csv 0408 0.1463629
# 10 C:/Users/r2/StackOverflow/13765634/sample2.csv 0410 0.1477264

Параметры: если вы предпочитаете только имя файла (без пути) и уверены, что конфликт имен файлов отсутствует, используйте вместо него set_names(basename(.)). (Этот шаг действительно необходим, если в любом случае используется имя файла в качестве имени столбца.) Я также удалю расширение файла, поскольку они, вероятно, все .csv или аналогичные.

file_list %>%
  # set_names(.) %>%
  set_names(tools::file_path_sans_ext(basename(.))) %>%
  map_dfr(~ read.csv(.x, header = FALSE, col.names = c("freq", "val")),
          .id = "filename") %>%
  mutate(freq = sprintf("%04.0f", freq))
#    filename freq       val
# 1   sample1 0402 0.1379457
# 2   sample1 0404 0.1390427
# 3   sample1 0406 0.1421666
# 4   sample1 0408 0.1463629
# 5   sample1 0410 0.1477264
# 6   sample2 0402 0.1379457
# 7   sample2 0404 0.1390427
# 8   sample2 0406 0.1421666
# 9   sample2 0408 0.1463629
# 10  sample2 0410 0.1477264

(Если вы нужно что-то делать с каждым набором данных за раз, тогда вы должны использовать %>% group_by(filename), не уверен, что это актуально.)

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

file_list %>%
  set_names(tools::file_path_sans_ext(basename(.))) %>%
  map(~ read.csv(.x, header = FALSE, col.names = c("freq", "val"))) %>%
  map2(., names(.), ~ transmute(.x, freq = sprintf("%04.0f", freq), !!.y := val))
# $sample1
#   freq   sample1
# 1 0402 0.1379457
# 2 0404 0.1390427
# 3 0406 0.1421666
# 4 0408 0.1463629
# 5 0410 0.1477264
# $sample2
#   freq   sample2
# 1 0402 0.1379457
# 2 0404 0.1390427
# 3 0406 0.1421666
# 4 0408 0.1463629
# 5 0410 0.1477264

Но я собираюсь вывести , что в конечном итоге вы хотите объединить эти столбцы, предполагая, что в столбце freq будет выравнивание. (Я не могу придумать другую причину, по которой вы хотите, чтобы имя столбца было именем файла.)

Для этого попробуйте это, вернувшись к первому использованию map_dfr, представляя pivot_wider:

file_list %>%
  set_names(tools::file_path_sans_ext(basename(.))) %>%
  map_dfr(~ read.csv(.x, header = FALSE, col.names = c("freq", "val")),
          .id = "filename") %>%
  mutate(freq = sprintf("%04.0f", freq)) %>%
  pivot_wider(freq, names_from = filename, values_from = val)
# # A tibble: 5 x 3
#   freq  sample1 sample2
#   <chr>   <dbl>   <dbl>
# 1 0402    0.138   0.138
# 2 0404    0.139   0.139
# 3 0406    0.142   0.142
# 4 0408    0.146   0.146
# 5 0410    0.148   0.148

Примечания (возможно, больше, чем soap -box):

  1. Что касается использования вами assign, я категорически не рекомендую такое поведение. Поскольку данные фактически структурированы одинаково, я предполагаю, что вы будете делать то же самое с каждым из этих файлов. В этом случае гораздо лучше использовать одну из функций *apply в списке из data.frame s . То есть вместо того, чтобы перебирать список имен переменных, get it, делать что-то, затем reassign it ... часто намного проще (программировать, читать, поддерживать) dats <- lapply(dats, some_function) или dats2 <- lapply(dats, function(x) { ...; x; }).

  2. Относительно использования имени файла как имени столбца. Некоторые инструменты (например, ggplot2) действительно выигрывают от наличия «длинных» данных (т. Е. Одного или нескольких столбцов категорий, таких как filename, и одного столбца для каждого типа данных ... тип зависит от вашего понимания данные). Возможно, вам будет полезно переосмыслить свое мнение о работе с этими данными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...