读取糟糕的 CSV 文件结构
Reading in Poor CSV File Structure
我正在尝试读取一个大型 csv 数据文件(由 , 分隔),但我一直卡在如下行中:
link 到原始文件:“http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv”
| RIN | UCR_Group | Loc |
|--------|-----------|---------------------------------------------|
|490658 | Property | 400 BLOCK 17TH ST, 5TH FL |
|506928 | Disorder | 1400 BLOCK W COLORADO AVE, COLORADO SPRINGS |
如您所见,列中也使用了文件的分隔符。有没有办法将列的分隔符指定为要在文件中读取的正则表达式,或者我是否需要研究使用 read.fwf 来查找每个字段的最大长度,并使用它解析数据?
目前,这是我到目前为止想出的代码:
datafile <- "http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv"
new <-readr::read_delim(datafile, sep ='[\S],[\S]')
new <-read.table(datafile, sep ='[\S],[\S]' )
我应该使用 read.fwf,还是尝试手动提取问题列?任何帮助将不胜感激。
编辑:
对于奖励积分,我真的很想构建一个函数来检测 csv 文件中的错误列,或者看起来可能会破坏文件结构的数据,例如这种情况。这样,我就不必乱用文本编辑器,并且可以通过编程方式在文件中找到这些错误。关于如何构建这样的东西的想法?
这是一个有效的示例,显示您可以使用正则表达式解析此文件,依赖于地址中的逗号具有 space 这一事实。如果这条规则并不总是成立,这当然会变得更加复杂:
txt <- "RIN,UCR_Group,Loc
123456,Property,1 STREET
490658,Property,400 BLOCK 17TH ST, 5TH FL
506928,Disorder,1400 BLOCK W COLORADO AVE, COLORADO SPRINGS"
dat <- readLines(textConnection(txt))
# in a real example:
# dat <- readLines("filename.csv")
spl <- strsplit(dat, "(?<=\S),(?=\S)", perl=TRUE)
setNames(data.frame(do.call(rbind, spl[-1])), spl[[1]])
# RIN UCR_Group Loc
#1 123456 Property 1 STREET
#2 490658 Property 400 BLOCK 17TH ST, 5TH FL
#3 506928 Disorder 1400 BLOCK W COLORADO AVE, COLORADO SPRINGS
使用 panda.read_csv
和正则表达式否定前瞻。同样的正则表达式也应该在 R
中工作。
import pandas as pd
df = pd.read_csv(filename, sep=r',(?!\s)')
过滤 df
以查找 LOC
包含逗号的行,以验证我们是否已正确解析:
df[df.LOC.str.contains(',')]
你知道哪个字段有非转义逗号:
library(stringi)
library(purrr)
txt <- readr::read_lines("http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv")
commas <- stri_locate_all_fixed(txt, ",")
map2_chr(txt[1:100], commas[1:100], function(x, y) {
len <- nrow(y)
bits <- c(1:6, (len-6):len)
for (i in bits) { stri_sub(x, y[i,1], y[i,2]) <- ";" }
x
}) -> rd
read.table(text=rd, header=TRUE, sep=";", stringsAsFactors=FALSE) %>%
dplyr::glimpse()
## Observations: 99
## Variables: 14
## $ RIN <int> 416667, 416673, 416674, 416680, 416684, 416686...
## $ UCR_Group <chr> "Crimes Against Property", "Crimes Against Per...
## $ UCR_Cat <chr> "Criminal Mischief/Damaged Property", "Forcibl...
## $ EXP_TRANSLATION <chr> "CRIMINAL MISCHIEF - MTR VEH", "SEX ASLT - RAP...
## $ OCC_DATE <chr> "1/1/2010 0:00:00", "1/1/2010 0:00:00", "1/1/2...
## $ OCC_TIME <int> 145, 300, 500, 730, 200, 440, 100, 851, 140, 2...
## $ LOC.TRANSLATION <chr> "200 BLOCK S ZENOBIA ST,VICTIM ", "1500 BL...
## $ PIN <int> 235602181, 219220590, 119013720, 174326399, 32...
## $ DOB <chr> "5/6/1979 0:00:00", "3/19/1988 0:00:00", "5/25...
## $ SEX <chr> "M", "M", "M", "M", "F", "F", "F", "F", "F", "...
## $ X <dbl> 3126041, 3134341, 3134360, 3127695, 3193317, 3...
## $ Y <dbl> 1684997, 1676360, 1700160, 1682545, 1708673, 1...
## $ LON <dbl> -105.05, -105.02, -105.02, -105.04, -104.81, -...
## $ LAT <dbl> 39.71, 39.68, 39.75, 39.70, 39.77, 39.78, 39.7...
将非space包围的每个逗号替换为分号,然后使用read.csv2
读入结果。
(将 Lines
替换为 readLines(u)
命令以从 u
读取它。此外,如果文件中有分号,则使用不同的字符并在 sep=
read.csv2
的参数或仅 read.csv
和 gsub
的第二个参数。)
read.csv2(text = gsub(",(\S)", ";\1", Lines)))
给予:
RIN UCR_Group Loc
1 490658 Property 400 BLOCK 17TH ST, 5TH FL
2 506928 Disorder 1400 BLOCK W COLORADO AVE, COLORADO SPRINGS
注:我们用这个作为输入Lines
:
Lines <- c("RIN,UCR_Group,Loc",
"490658,Property,400 BLOCK 17TH ST, 5TH FL",
"506928,Disorder,1400 BLOCK W COLORADO AVE, COLORADO SPRINGS")
更新: 在实际文件中似乎 space 可以出现在有效的逗号分隔符之前,并且只有一个字符字段,因此我们相应地修改了模式。这是文件前 3 行的结果:
u <- "http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv"
Lines <- readLines(u, 3)
read.csv2(text = gsub(",(\S)", ";\1", Lines))
给予:
RIN UCR_Group UCR_Cat
1 416667 Crimes Against Property Criminal Mischief/Damaged Property
2 416673 Crimes Against Persons Forcible Sex Offenses
EXP_TRANSLATION OCC_DATE OCC_TIME LOC
1 CRIMINAL MISCHIEF - MTR VEH 1/1/2010 0:00:00 145 200 BLOCK S ZENOBIA ST
2 SEX ASLT - RAPE 1/1/2010 0:00:00 300 1500 BLOCK S DECATUR ST
TRANSLATION PIN DOB SEX X Y LON
1 VICTIM 235602181 5/6/1979 0:00:00 M 3126041.08 1684996.73 -105.05
2 ARRESTEE 219220590 3/19/1988 0:00:00 M 3134340.56 1676360.06 -105.02
LAT
1 39.71
2 39.68
我正在尝试读取一个大型 csv 数据文件(由 , 分隔),但我一直卡在如下行中: link 到原始文件:“http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv”
| RIN | UCR_Group | Loc |
|--------|-----------|---------------------------------------------|
|490658 | Property | 400 BLOCK 17TH ST, 5TH FL |
|506928 | Disorder | 1400 BLOCK W COLORADO AVE, COLORADO SPRINGS |
如您所见,列中也使用了文件的分隔符。有没有办法将列的分隔符指定为要在文件中读取的正则表达式,或者我是否需要研究使用 read.fwf 来查找每个字段的最大长度,并使用它解析数据? 目前,这是我到目前为止想出的代码:
datafile <- "http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv"
new <-readr::read_delim(datafile, sep ='[\S],[\S]')
new <-read.table(datafile, sep ='[\S],[\S]' )
我应该使用 read.fwf,还是尝试手动提取问题列?任何帮助将不胜感激。
编辑:
对于奖励积分,我真的很想构建一个函数来检测 csv 文件中的错误列,或者看起来可能会破坏文件结构的数据,例如这种情况。这样,我就不必乱用文本编辑器,并且可以通过编程方式在文件中找到这些错误。关于如何构建这样的东西的想法?
这是一个有效的示例,显示您可以使用正则表达式解析此文件,依赖于地址中的逗号具有 space 这一事实。如果这条规则并不总是成立,这当然会变得更加复杂:
txt <- "RIN,UCR_Group,Loc
123456,Property,1 STREET
490658,Property,400 BLOCK 17TH ST, 5TH FL
506928,Disorder,1400 BLOCK W COLORADO AVE, COLORADO SPRINGS"
dat <- readLines(textConnection(txt))
# in a real example:
# dat <- readLines("filename.csv")
spl <- strsplit(dat, "(?<=\S),(?=\S)", perl=TRUE)
setNames(data.frame(do.call(rbind, spl[-1])), spl[[1]])
# RIN UCR_Group Loc
#1 123456 Property 1 STREET
#2 490658 Property 400 BLOCK 17TH ST, 5TH FL
#3 506928 Disorder 1400 BLOCK W COLORADO AVE, COLORADO SPRINGS
使用 panda.read_csv
和正则表达式否定前瞻。同样的正则表达式也应该在 R
中工作。
import pandas as pd
df = pd.read_csv(filename, sep=r',(?!\s)')
过滤 df
以查找 LOC
包含逗号的行,以验证我们是否已正确解析:
df[df.LOC.str.contains(',')]
你知道哪个字段有非转义逗号:
library(stringi)
library(purrr)
txt <- readr::read_lines("http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv")
commas <- stri_locate_all_fixed(txt, ",")
map2_chr(txt[1:100], commas[1:100], function(x, y) {
len <- nrow(y)
bits <- c(1:6, (len-6):len)
for (i in bits) { stri_sub(x, y[i,1], y[i,2]) <- ";" }
x
}) -> rd
read.table(text=rd, header=TRUE, sep=";", stringsAsFactors=FALSE) %>%
dplyr::glimpse()
## Observations: 99
## Variables: 14
## $ RIN <int> 416667, 416673, 416674, 416680, 416684, 416686...
## $ UCR_Group <chr> "Crimes Against Property", "Crimes Against Per...
## $ UCR_Cat <chr> "Criminal Mischief/Damaged Property", "Forcibl...
## $ EXP_TRANSLATION <chr> "CRIMINAL MISCHIEF - MTR VEH", "SEX ASLT - RAP...
## $ OCC_DATE <chr> "1/1/2010 0:00:00", "1/1/2010 0:00:00", "1/1/2...
## $ OCC_TIME <int> 145, 300, 500, 730, 200, 440, 100, 851, 140, 2...
## $ LOC.TRANSLATION <chr> "200 BLOCK S ZENOBIA ST,VICTIM ", "1500 BL...
## $ PIN <int> 235602181, 219220590, 119013720, 174326399, 32...
## $ DOB <chr> "5/6/1979 0:00:00", "3/19/1988 0:00:00", "5/25...
## $ SEX <chr> "M", "M", "M", "M", "F", "F", "F", "F", "F", "...
## $ X <dbl> 3126041, 3134341, 3134360, 3127695, 3193317, 3...
## $ Y <dbl> 1684997, 1676360, 1700160, 1682545, 1708673, 1...
## $ LON <dbl> -105.05, -105.02, -105.02, -105.04, -104.81, -...
## $ LAT <dbl> 39.71, 39.68, 39.75, 39.70, 39.77, 39.78, 39.7...
将非space包围的每个逗号替换为分号,然后使用read.csv2
读入结果。
(将 Lines
替换为 readLines(u)
命令以从 u
读取它。此外,如果文件中有分号,则使用不同的字符并在 sep=
read.csv2
的参数或仅 read.csv
和 gsub
的第二个参数。)
read.csv2(text = gsub(",(\S)", ";\1", Lines)))
给予:
RIN UCR_Group Loc
1 490658 Property 400 BLOCK 17TH ST, 5TH FL
2 506928 Disorder 1400 BLOCK W COLORADO AVE, COLORADO SPRINGS
注:我们用这个作为输入Lines
:
Lines <- c("RIN,UCR_Group,Loc",
"490658,Property,400 BLOCK 17TH ST, 5TH FL",
"506928,Disorder,1400 BLOCK W COLORADO AVE, COLORADO SPRINGS")
更新: 在实际文件中似乎 space 可以出现在有效的逗号分隔符之前,并且只有一个字符字段,因此我们相应地修改了模式。这是文件前 3 行的结果:
u <- "http://daniels-pull.universityofdenv.netdna-cdn.com/assets/GeneralOccurrencesAll.csv"
Lines <- readLines(u, 3)
read.csv2(text = gsub(",(\S)", ";\1", Lines))
给予:
RIN UCR_Group UCR_Cat
1 416667 Crimes Against Property Criminal Mischief/Damaged Property
2 416673 Crimes Against Persons Forcible Sex Offenses
EXP_TRANSLATION OCC_DATE OCC_TIME LOC
1 CRIMINAL MISCHIEF - MTR VEH 1/1/2010 0:00:00 145 200 BLOCK S ZENOBIA ST
2 SEX ASLT - RAPE 1/1/2010 0:00:00 300 1500 BLOCK S DECATUR ST
TRANSLATION PIN DOB SEX X Y LON
1 VICTIM 235602181 5/6/1979 0:00:00 M 3126041.08 1684996.73 -105.05
2 ARRESTEE 219220590 3/19/1988 0:00:00 M 3134340.56 1676360.06 -105.02
LAT
1 39.71
2 39.68