【问题标题】:Import Mixed CSV that has quotes around text导入包含文本引号的混合 CSV
【发布时间】:2014-12-17 02:59:30
【问题描述】:

我正在将逗号分隔的 CSV 文件导入 MATLAB。每列都有引号,我想将其视为文本,然后是逗号。

我正在使用这个问题的答案中的 read_mixed_csv 函数以单元格的形式读取数据:Import CSV file with mixed data types

thisdata = read_mixed_csv(fname, ','); % Reads in the CSV file 
thisdata = regexprep(thisdata, '^"|"$','');

但是,由于我的一些专栏看起来像这样:

"FAIRHOPE, Alabama"
"FAIRHOPE HIGH SCHOOL, FAIRHOPE,  ALABAMA"
"Daphne-Fairhope-Foley, AL"

MATLAB 将逗号后的所有内容放入一个新列中。所以

"Daphne-Fairhope-Foley, AL"

变成两列

"Daphne-Fairhope-Foley
AL"

如何让 MATLAB 读取混合的 csv 文件,并且不仅将逗号视为分隔符,还考虑引号? 有没有比 @ 更自动化的方法? 987654326@?如果textscan 是一个选项,那会是什么样子?

这是我尝试读取的数据示例,其中包含标题:

"State Code","County Code","Site Num","Parameter Code","POC","Latitude","Longitude","Datum","Parameter Name","Sample Duration","Pollutant Standard","Date Local","Units of Measure","Event Type","Observation Count","Observation Percent","Arithmetic Mean","1st Max Value","1st Max Hour","AQI","Method Name","Local Site Name","Address","State Name","County Name","City Name","CBSA Name","Date of Last Change"
"01","003","0010","88101",1,30.498001,-87.881412,"NAD83","PM2.5 - Local Conditions","24 HOUR","PM25 24-hour 2006","2013-01-01","Micrograms/cubic meter (LC)","None",1,100.0,7.3,7.3,0,30,"R & P Model 2025 PM2.5 Sequential w/WINS - GRAVIMETRIC","FAIRHOPE, Alabama","FAIRHOPE HIGH SCHOOL, FAIRHOPE,  ALABAMA","Alabama","Baldwin","Fairhope","Daphne-Fairhope-Foley, AL","2014-02-11"
"01","003","0010","88101",1,30.498001,-87.881412,"NAD83","PM2.5 - Local Conditions","24 HOUR","PM25 24-hour 2006","2013-01-04","Micrograms/cubic meter (LC)","None",1,100.0,7.6,7.6,0,32,"R & P Model 2025 PM2.5 Sequential w/WINS - GRAVIMETRIC","FAIRHOPE, Alabama","FAIRHOPE HIGH SCHOOL, FAIRHOPE,  ALABAMA","Alabama","Baldwin","Fairhope","Daphne-Fairhope-Foley, AL","2014-02-11"
"01","003","0010","88101",1,30.498001,-87.881412,"NAD83","PM2.5 - Local Conditions","24 HOUR","PM25 24-hour 2006","2013-01-07","Micrograms/cubic meter (LC)","None",1,100.0,8.6,8.6,0,36,"R & P Model 2025 PM2.5 Sequential w/WINS - GRAVIMETRIC","FAIRHOPE, Alabama","FAIRHOPE HIGH SCHOOL, FAIRHOPE,  ALABAMA","Alabama","Baldwin","Fairhope","Daphne-Fairhope-Foley, AL","2014-02-11"
"01","003","0010","88101",1,30.498001,-87.881412,"NAD83","PM2.5 - Local Conditions","24 HOUR","PM25 24-hour 2006","2013-01-10","Micrograms/cubic meter (LC)","None",1,100.0,7,7,0,29,"R & P Model 2025 PM2.5 Sequential w/WINS - GRAVIMETRIC","FAIRHOPE, Alabama","FAIRHOPE HIGH SCHOOL, FAIRHOPE,  ALABAMA","Alabama","Baldwin","Fairhope","Daphne-Fairhope-Foley, AL","2014-02-11"

*注意:将 CSV 文件转换为制表符分隔文件可以让 MATLAB 更轻松地处理和规避此问题。

【问题讨论】:

    标签: matlab csv import


    【解决方案1】:

    使用文本限定符(如 ")有点棘手,但如果您确保表格的每一行都具有相同数量的列(并且可能没有空列),则以下方法可能会起作用。

    文本限定符之外的任何内容都必须可转换为数字。

    function C = csvmixed(eachLine,delim,textQualifier)
    % Outputs cell containing mixed string and numeric data given a delimiter (',') 
    % and a text qualifier ('"').  Each line of the delimited file must be loaded into 
    % the cell array eachLine, and each line must have the same number of columns.
    % 
    % Example:
    %   fid = fopen('testcsv.txt','r');
    %   eachLine = textscan(fid,'%s','Delimiter','\n'); fclose(fid);
    %   C = csvmixed(eachLine{1},',','"')
    
    assert(ischar(delim) && numel(delim)==1);
    assert(ischar(textQualifier) && numel(textQualifier)==1);
    
    % find strings, as specified by the input qualifier
    patternStr = sprintf('"([^"]*)"%c?',delim);
    patternStr = strrep(patternStr,'"',textQualifier);
    Cstr = regexp(eachLine,patternStr,'tokens');
    
    % find numeric data
    patternNum = sprintf('(?<=(,|^))[^%c,a-zA-Z]*(?=(,|$))',textQualifier);
    patternNum = strrep(patternNum,',',delim);
    Cnum = regexp(eachLine,patternNum,'match','emptymatch');
    
    numCols = cellfun(@numel,Cstr) + cellfun(@numel,Cnum);
    assert(nnz(diff(numCols))==0,'Number of columns not consistent.')
    
    % get string extents (begin, start) indexes for each string
    strExtents = regexp(eachLine,patternStr,'tokenExtents');
    
    % deal out parsed data for each line
    C = cell(numel(eachLine),numCols(1));
    for ii = 1:numel(eachLine),
        strBounds = vertcat(strExtents{ii}{:});
        delimLocs = getDelimLocs(eachLine{ii},strBounds,delim);
        strCellMap = getCellMap(strBounds,delimLocs);
    
        C(ii,strCellMap) = [Cstr{ii}{:}]; % TODO: preallocate
        C(ii,~strCellMap) = num2cell(str2double(Cnum{ii})); % all else must be numeric
    end
    
    end
    
    function delimLocs = getDelimLocs(lineText,solidBounds,delim)
        delimCharLocs = strfind(lineText,delim);
        delimLocs = delimCharLocs(~any(bsxfun(@ge,delimCharLocs,solidBounds(:,1)) & ...
            bsxfun(@le,delimCharLocs,solidBounds(:,2)),1));
    end
    
    function cellMap = getCellMap(typeBounds,delimLocs)
        cellMap = any(bsxfun(@gt,typeBounds(:,1),[0 delimLocs]) & ...
            bsxfun(@lt,typeBounds(:,1),[delimLocs Inf]), 1);
    end
    

    更新:修正getDelimLocs 中的小错别字。添加元胞数组的预分配。

    【讨论】:

    • 这适用于我的几个小文件。但随后它开始给我一个“csvmixed 中的错误(第 34 行)C(l,~strCellMap) = num2cell(str2double(Cnum{l})); % all else must be numeric。”这些文件的格式基本相同。您知道错误指的是什么吗?这是其中一个文件:dropbox.com/s/c5ece8pjsg6sag4/daily_88101_1999.csv?dl=0
    • @shizishan 您的文件没有根据链接上传完成,但我可以告诉您它与此有关:“任何不在文本限定符内的内容都必须转换为数字。 " 查找不在引号中的非数字数据。可以添加单独的逻辑来处理非数字,但它会变得更复杂和更慢。
    • 我一直在尝试查看是否是问题所在,但看起来文件的任何部分都没有不同。我意识到,对于 2000 年,它在所有 01 097 2005 之后停止(前三列告诉我数据来自哪个站点)。我会继续努力,但如果你有机会,我把 2000 年的文件放在这里。这次应该可以了:mega.co.nz/…
    • @shizishan 有个小错误。该代码不处理以逗号结尾的字符串(例如"501 W. VALLEY BLVD., BIG BEAR CITY,")。它是固定的。仅供参考,该文件的输出单元约为 580 MB。
    • @shizishan 不确定,我只安装了2014版本。
    【解决方案2】:

    使用文件交换代码replaceinfile 将其中包含逗号的字符串替换为句点。 使用Import CSV file with mixed data types 中的read_mixed_csv 读取文件。 从仍然留下的字符串中删除多余的引号。

    replaceinfile(', ', '. ', fname); % Replace commas that was inside quotes and not meant to be separated as periods so they don't show up as a new column
    thisdata = read_mixed_csv(fname, ','); % Reads in the CSV file (\t for tab)
    thisdata = regexprep(thisdata, '^"|"$',''); % Remove quotes from file and only keep the first 28 columns (last two columns are empty)
    

    对于replaceinfile.m 函数: 要在 Linux 上运行代码,请将 Perl 部分的第一行更改为

    perlCmd = sprintf('"%s"', '/usr/bin/perl');
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-31
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 2016-01-20
      • 2012-03-22
      • 1970-01-01
      相关资源
      最近更新 更多