Как читать данные, когда некоторые числа содержат запятые в качестве разделителя тысяч?

117

У меня есть файл csv, в котором некоторые числовые значения выражены в виде строк с запятыми в качестве разделителя тысяч, например, "1,513"вместо 1513. Каков самый простой способ прочитать данные в R?

Я могу использовать read.csv(..., colClasses="character"), но тогда мне нужно удалить запятые из соответствующих элементов, прежде чем преобразовывать эти столбцы в числовые, и я не могу найти аккуратный способ сделать это.

Роб Хайндман
источник

Ответы:

141

Не уверен , что о том , чтобы read.csvинтерпретировать это правильно, но вы можете использовать , gsubчтобы заменить ","с "", а затем преобразовать строку с numericпомощью as.numeric:

y <- c("1,200","20,000","100","12,111")
as.numeric(gsub(",", "", y))
# [1]  1200 20000 100 12111

На этот вопрос также ответили ранее в R-Help (и во втором квартале здесь ).

Кроме того, вы можете предварительно обработать файл, например, с помощью sedunix.

Шейн
источник
60

Вы можете использовать read.table или read.csv для выполнения этого преобразования за вас полуавтоматически. Сначала создайте определение нового класса, затем создайте функцию преобразования и установите ее как метод «as», используя функцию setAs, например:

setClass("num.with.commas")
setAs("character", "num.with.commas", 
        function(from) as.numeric(gsub(",", "", from) ) )

Затем запустите read.csv, например:

DF <- read.csv('your.file.here', 
   colClasses=c('num.with.commas','factor','character','numeric','num.with.commas'))
Грег Сноу
источник
3
Это очень красивый трюк. Его можно использовать для преобразования при импорте (например, преобразование значений Y / N в логический вектор с использованием setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] )).
Марек
1
Тот же трюк используется в аналогичной задаче . И чтобы добавить: можно было использовать либо, setClass("num.with.commas")либо, suppresMessage(setAs(.....))чтобы избежать сообщения о пропущенном классе.
Марек
Привет, Грег, спасибо, что поделился этой удобной функцией. После выполнения я получаю следующее предупреждение: в методе для 'coerce' с подписью '«character», «num.with.commas»': нет определения для класса «num.with.commas». Любое представление о том, в чем проблема, У меня есть твой код слово в слово?
TheGoat
Я проверил ссылку на аналогичную проблему и увидел, что мне нужно установить класс! Спасибо за ловкий трюк.
TheGoat
17

Я хочу использовать R вместо предварительной обработки данных, так как это упрощает проверку данных. Следуя предложению Шейна об использовании gsub, я думаю, что это настолько аккуратно, насколько я могу:

x <- read.csv("file.csv",header=TRUE,colClasses="character")
col2cvt <- 15:41
x[,col2cvt] <- lapply(x[,col2cvt],function(x){as.numeric(gsub(",", "", x))})
Роб Хайндман
источник
Разве colClasses = "char" не заставляет все столбцы быть char, и в этом случае другие, кроме 15:41, также являются char? Возможно, позволив read.csv () решить, а затем преобразовав те, которые в столбце 15:41, вы получите «больше» числовых столбцов.
Дирк Эддельбюттель,
Да, но, как отмечалось в моем вопросе, все остальные столбцы являются символами. Вместо этого я мог бы использовать as.is = TRUE, что было бы более общим. Но разрешение read.csv () принимать решение с использованием аргументов по умолчанию бесполезно, потому что он преобразует все, что выглядит как символ, в фактор, который вызывает проблемы для числовых столбцов, поскольку тогда они не преобразуются должным образом с использованием as.numeric () ,
Роб Хайндман,
Вам следует рассмотреть возможность установки аргумента dec = в таблице чтения на ".". Это значение по умолчанию для read.csv2, но запятая встроена в read.csv ().
IRTFM
15

Этому вопросу несколько лет, но я наткнулся на него, а значит, возможно, другие ответят.

В readrбиблиотеке / пакете есть несколько хороших функций. Один из них - хороший способ интерпретировать такие «беспорядочные» столбцы.

library(readr)
read_csv("numbers\n800\n\"1,800\"\n\"3500\"\n6.5",
          col_types = list(col_numeric())
        )

Это дает

Источник: локальный фрейм данных [4 x 1]

  numbers
    (dbl)
1   800.0
2  1800.0
3  3500.0
4     6.5

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

Например, если бы я не отмечал col_types, я бы получил следующее:

> read_csv("numbers\n800\n\"1,800\"\n\"3500\"\n6.5")
Source: local data frame [4 x 1]

  numbers
    (chr)
1     800
2   1,800
3    3500
4     6.5

(Обратите внимание, что теперь это chr( character) вместо a numeric.)

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

> set.seed(1)
> tmp <- as.character(sample(c(1:10), 100, replace=TRUE))
> tmp <- c(tmp, "1,003")
> tmp <- paste(tmp, collapse="\"\n\"")

(так, чтобы последние несколько элементов выглядели так :)

\"5\"\n\"9\"\n\"7\"\n\"1,003"

Тогда у вас вообще возникнут проблемы с чтением запятой!

> tail(read_csv(tmp))
Source: local data frame [6 x 1]

     3"
  (dbl)
1 8.000
2 5.000
3 5.000
4 9.000
5 7.000
6 1.003
Warning message:
1 problems parsing literal data. See problems(...) for more details. 
Майк Уильямсон
источник
7

dplyrрешение с использованием mutate_allи труб

скажем, у вас есть следующее:

> dft
Source: local data frame [11 x 5]

   Bureau.Name Account.Code   X2014   X2015   X2016
1       Senate          110 158,000 211,000 186,000
2       Senate          115       0       0       0
3       Senate          123  15,000  71,000  21,000
4       Senate          126   6,000  14,000   8,000
5       Senate          127 110,000 234,000 134,000
6       Senate          128 120,000 159,000 134,000
7       Senate          129       0       0       0
8       Senate          130 368,000 465,000 441,000
9       Senate          132       0       0       0
10      Senate          140       0       0       0
11      Senate          140       0       0       0

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

dft %>%
    mutate_all(funs(as.character(.)), X2014:X2016) %>%
    mutate_all(funs(gsub(",", "", .)), X2014:X2016) %>%
    mutate_all(funs(as.numeric(.)), X2014:X2016)

mutate_allприменяет функцию (ы) внутри funsк указанным столбцам

Я делал это последовательно, по одной функции за раз (если вы используете несколько функций внутри, funsвы создаете дополнительные ненужные столбцы)

Павел
источник
3
mutate_eachустарела. Вы хотите обновить свой ответ с помощью mutate_atили похожего?
T_T
6

«Предварительная обработка» в R:

lines <- "www, rrr, 1,234, ttt \n rrr,zzz, 1,234,567,987, rrr"

Можно использовать readLinesна textConnection. Затем удалите только запятые между цифрами:

gsub("([0-9]+)\\,([0-9])", "\\1\\2", lines)

## [1] "www, rrr, 1234, ttt \n rrr,zzz, 1234567987, rrr"

Также полезно знать, но не имеет прямого отношения к этому вопросу, что запятые как десятичные разделители могут обрабатываться read.csv2 (автоматически) или read.table (с установкой параметра dec).

Изменить: позже я обнаружил, как использовать colClasses, создав новый класс. Видеть:

Как загрузить df с разделителем 1000 в R как числовой класс?

IRTFM
источник
Спасибо, это был хороший указатель, но он не работает для цифр, содержащих несколько десятичных знаков, например, 1234567,89 - необходимо было обойти эту проблему, чтобы импортировать электронную таблицу Google в R, см. Stackoverflow.com/a/30020171/3096626 для простого функция, которая выполняет работу с несколькими десятичными знаками
flexponsive
4

Если число разделено знаком "." и десятичные знаки "," (1.200.000,00) при вызове gsubнеобходимоset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))

аса
источник
3

Очень удобный способ - readr::read_delimсемья. Взяв пример отсюда: Импортируя csv с несколькими разделителями в R, вы можете сделать это следующим образом:

txt <- 'OBJECTID,District_N,ZONE_CODE,COUNT,AREA,SUM
1,Bagamoyo,1,"136,227","8,514,187,500.000000000000000","352,678.813105723350000"
2,Bariadi,2,"88,350","5,521,875,000.000000000000000","526,307.288878142830000"
3,Chunya,3,"483,059","30,191,187,500.000000000000000","352,444.699742995200000"'

require(readr)
read_csv(txt) # = read_delim(txt, delim = ",")

Что дает ожидаемый результат:

# A tibble: 3 × 6
  OBJECTID District_N ZONE_CODE  COUNT        AREA      SUM
     <int>      <chr>     <int>  <dbl>       <dbl>    <dbl>
1        1   Bagamoyo         1 136227  8514187500 352678.8
2        2    Bariadi         2  88350  5521875000 526307.3
3        3     Chunya         3 483059 30191187500 352444.7
Rentrop
источник
3

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

locale = locale(decimal_mark = ",")

read_delim("filetoread.csv", ';", locale = locale(decimal_mark = ","))

* Точка с запятой во второй строке означает, что read_delim будет читать значения, разделенные точкой с запятой csv.

Это поможет прочитать все числа с запятой как правильные числа.

С уважением

Матеуш Каниа

Матеуш Каниа
источник
3

Мы также можем использовать readr::parse_number, хотя столбцы должны быть символами. Если мы хотим применить его для нескольких столбцов, мы можем перебирать столбцы, используяlapply

df[2:3] <- lapply(df[2:3], readr::parse_number)
df

#  a        b        c
#1 a    12234       12
#2 b      123  1234123
#3 c     1234     1234
#4 d 13456234    15342
#5 e    12312 12334512

Или используйте mutate_atfrom, dplyrчтобы применить его к определенным переменным.

library(dplyr)
df %>% mutate_at(2:3, readr::parse_number)
#Or
df %>% mutate_at(vars(b:c), readr::parse_number)

данные

df <- data.frame(a = letters[1:5], 
                 b = c("12,234", "123", "1,234", "13,456,234", "123,12"),
                 c = c("12", "1,234,123","1234", "15,342", "123,345,12"), 
                 stringsAsFactors = FALSE)
Ронак Шах
источник
1

Я думаю, что предварительная обработка - это лучший способ. Вы можете использовать Notepad ++, у которого есть опция замены регулярного выражения.

Например, если ваш файл был таким:

"1,234","123","1,234"
"234","123","1,234"
123,456,789

Затем вы можете использовать регулярное выражение "([0-9]+),([0-9]+)"и заменить его на\1\2

1234,"123",1234
"234","123",1234
123,456,789

Затем вы можете использовать x <- read.csv(file="x.csv",header=FALSE)для чтения файла.

Иаков
источник
22
Все, что вы можете написать, вы должны. Выполнение этого вручную создает возможность ошибки, а также снижает воспроизводимость.
Хэдли 07