Я пытаюсь импортировать CSV с пробелами, читаемыми как "". К сожалению, сейчас они все читают как "NA".

Чтобы лучше продемонстрировать проблему, я также показываю, как NA, "NA" и "" все сопоставляются с одним и тем же (кроме самого нижнего примера), что предотвратит простой обходной путь dt[is.na(dt)] <- ""

> write.csv(matrix(c("0","",NA,"NA"),ncol = 2),"MRE.csv")

Открывая это в блокноте, это выглядит так

"","V1","V2"
"1","0",NA
"2","","NA"

Итак, читаю это обратно ...

> fread("MRE.csv")
   V1 V1 V2
1:  1  0 NA
2:  2 NA NA

Документация, кажется, предлагает это, но это не работает, как описано

> fread("MRE.csv",na.strings = NULL)
   V1 V1 V2
1:  1  0 NA
2:  2 NA NA

Также пробовал это, которое считывает NA как фактическое NA, но проблема остается для пустой строки, которая читается как "NA"

> fread("MRE.csv",colClasses=c(V1="character",V2="character"))
   V1 V1   V2
1:  1  0 <NA>
2:  2 NA   NA

> fread("MRE.csv",colClasses=c(V1="character",V2="character"))[,V2]
[1] NA   "NA"

Data.table версия 1.11.4

R версия 3.5.1

r
2
hedgedandlevered 3 Окт 2019 в 02:06

1 ответ

Лучший ответ

Здесь происходит несколько возможных вещей:

  1. Независимо от того, что вы пишете здесь "0", функция чтения (fread) делает вывод на основе просмотра части файла. Это не редкость (readr тоже делает), и это можно контролировать (с colClasses=).

  2. Это может быть уникальным для вашего вопроса здесь (а не для ваших реальных данных), но ваш вызов write.csv неявно помещает буквальные буквы NA в файл (не путать с "NA" где у вас есть буквальная строка). Это может сбивать с толку, даже если вы переопределите с помощью colClasses=.

  3. Возможно, вы уже знаете это, но поскольку fread предполагает, что эти столбцы на самом деле являются integer классами, они не могут содержать пустые строки: после определения числового столбца все, что не является числовым, будет NA.

Давайте переделаем вашу первую сторону генерации csv, чтобы убедиться, что мы не запутаем ситуацию.

write.csv(matrix(c("0","",NA,"NA"),ncol = 2), "MRE.csv", na="")

(Ниже я использую оператор канала magrittr %>% только для презентации, это не обязательно.)

Первый пример демонстрирует вывод fread. Второй показывает, что мы переопределяем это поведение, и теперь у нас есть пустые строки в каждом месте NA, которое не является буквальной строкой "NA".

fread("MRE.csv") %>% str
# Classes 'data.table' and 'data.frame':    2 obs. of  3 variables:
#  $ V1: int  1 2
#  $ V1: int  0 NA
#  $ V2: logi  NA NA
#  - attr(*, ".internal.selfref")=<externalptr> 

fread("MRE.csv", colClasses="character") %>% str
# Classes 'data.table' and 'data.frame':    2 obs. of  3 variables:
#  $ V1: chr  "1" "2"
#  $ V1: chr  "0" ""
#  $ V2: chr  "" "NA"
#  - attr(*, ".internal.selfref")=<externalptr> 

Это также можно контролировать для отдельных столбцов. Одна проблема с этим примером заключается в том, что fread по какой-то причине заставляет столбец с именами строк называться V1, так же, как и следующий столбец. Мне это кажется ошибкой, возможно, вы можете посмотреть Проблемы Rdatatable и, возможно, опубликуйте новую. (Я могу ошибаться, возможно, это преднамеренное / известное поведение.)

Из-за этого переопределение для каждого столбца, кажется, останавливается при первом появлении имени столбца.

fread("MRE.csv", colClasses=c(V1="character", V2="character")) %>% str
# Classes 'data.table' and 'data.frame':    2 obs. of  3 variables:
#  $ V1: chr  "1" "2"
#  $ V1: int  0 NA
#  $ V2: chr  "" "NA"
#  - attr(*, ".internal.selfref")=<externalptr> 

Один из способов обойти это - использовать безымянный вектор, требующий того же количества классов, что и количество столбцов:

fread("MRE.csv", colClasses=c("character","character","character")) %>% str
# Classes 'data.table' and 'data.frame':    2 obs. of  3 variables:
#  $ V1: chr  "1" "2"
#  $ V1: chr  "0" ""
#  $ V2: chr  "" "NA"
#  - attr(*, ".internal.selfref")=<externalptr> 

Другой способ (спасибо @thelatemail) - список:

fread("MRE.csv", colClasses=list(character=2:3)) %>% str
# Classes 'data.table' and 'data.frame':    2 obs. of  3 variables:
#  $ V1: int  1 2
#  $ V1: chr  "0" ""
#  $ V2: chr  "" "NA"
#  - attr(*, ".internal.selfref")=<externalptr> 

Боковое примечание: если вам нужно сохранить их как ints / nums, тогда:

  1. если вас беспокоит то, как это влияет на последующие расчеты, вы можете:

    1. исправить источник данных, чтобы не указывать значения NULL;
    2. отфильтровать неполные наблюдения (строки); или
    3. исправить вычисления, чтобы разумно обрабатывать недостающие данные.
  2. если вас беспокоит то, как это выглядит в отчете, то в любом инструменте, который вы используете для отображения в отчете, должен быть механизм для отображения значений NA; например, установка options(knitr.kable.NA="") перед knitr::kable(...) представит их как пустые строки.

  3. Если вас беспокоит то, как это выглядит на консоли , у вас есть два варианта:

    1. вмешиваться в данные, перебирая каждый (предполагаемый) столбец и изменяя значения NA на ""; это работает только со столбцами character и необратимо; или
    2. напишите свой собственный подкласс data.frame, который изменяет его отображение на консоли; Преимущество этого в том, что он не разрушает; проблема в том, что вам нужно переклассифицировать каждый объект, для которого вы хотите это поведение, и большинство (если не все) функций, которые выводят кадры, скорее всего, непреднамеренно удаляют этот класс из вашего ввода. (Для этого вам нужно написать S3-метод print для вашего подкласса.)
2
r2evans 2 Окт 2019 в 23:44