Импорт файла CSV со смешанными типами данных

36

Я работаю с MATLAB в течение нескольких дней, и мне трудно импортировать CSV файл в матрицу.

Моя проблема в том, что мой CSV файл содержит почти только строки и некоторые целочисленные значения, поэтому csvread() не работает. csvread() поддерживает только целые значения.

Как я могу хранить свои строки в каком-то двумерном массиве, чтобы иметь свободный доступ к каждому элементу?

Вот пример CSV для моих нужд:

04;abc;def;ghj;klm;;;;;
;;;;;Test;text;0xFF;;
;;;;;asdfhsdf;dsafdsag;0x0F0F;;

Главное - пустые ячейки и тексты внутри ячеек. Как видите, структура может меняться.

  • 3
    Посмотрите тексты> mathworks.com/help/techdoc/ref/textscan.html
  • 2
    Вы должны опубликовать пару строк из вашего CSV-файла, чтобы мы получили представление о том, как структурирован файл (строки, заключенные в ' или " , содержат ли они запятые внутри кавычек, также являются целочисленными значениями в кавычках, есть ли любые экранированные разделители, такие как "this is \"an example\"" т. д.)
Показать ещё 1 комментарий
Теги:
csv
file-io
import
parsing

9 ответов

49
Лучший ответ

Отредактировано, что вопрос был обновлен с помощью примерного входного файла...

В случае, когда вы знаете, сколько столбцов данных будет в вашем CSV файле, один простой вызов TEXTSCAN Amro предлагает будет вашим лучшим решением.

Однако, если вы не знаете априори, сколько столбцов находится в вашем файле, вы можете использовать более общий подход, как я сделал в следующей функции. Я сначала использовал функцию FGETL, чтобы прочитать каждую строку файла в массиве ячеек. Затем я использовал функцию TEXTSCAN для синтаксического анализа каждой строки в отдельных строках с использованием предопределенного разделителя полей и обработки целых полей как строк на данный момент ( они могут быть преобразованы в числовые значения позже). Вот результирующий код, помещенный в функцию read_mixed_csv:

function lineArray = read_mixed_csv(fileName,delimiter)
  fid = fopen(fileName,'r');   %# Open the file
  lineArray = cell(100,1);     %# Preallocate a cell array (ideally slightly
                               %#   larger than is needed)
  lineIndex = 1;               %# Index of cell to place the next line in
  nextLine = fgetl(fid);       %# Read the first line from the file
  while ~isequal(nextLine,-1)         %# Loop while not at the end of the file
    lineArray{lineIndex} = nextLine;  %# Add the line to the cell array
    lineIndex = lineIndex+1;          %# Increment the line index
    nextLine = fgetl(fid);            %# Read the next line from the file
  end
  fclose(fid);                 %# Close the file
  lineArray = lineArray(1:lineIndex-1);  %# Remove empty cells, if needed
  for iLine = 1:lineIndex-1              %# Loop over lines
    lineData = textscan(lineArray{iLine},'%s',...  %# Read strings
                        'Delimiter',delimiter);
    lineData = lineData{1};              %# Remove cell encapsulation
    if strcmp(lineArray{iLine}(end),delimiter)  %# Account for when the line
      lineData{end+1} = '';                     %#   ends with a delimiter
    end
    lineArray(iLine,1:numel(lineData)) = lineData;  %# Overwrite line data
  end
end

Запуск этой функции из содержимого файла образца из вопроса дает следующий результат:

>> data = read_mixed_csv('myfile.csv',';')

data = 

  Columns 1 through 7

    '04'    'abc'    'def'    'ghj'    'klm'    ''            ''        
    ''      ''       ''       ''       ''       'Test'        'text'    
    ''      ''       ''       ''       ''       'asdfhsdf'    'dsafdsag'

  Columns 8 through 10

    ''          ''    ''
    '0xFF'      ''    ''
    '0x0F0F'    ''    ''

В результате получается массив размером 3 на 10 с одним полем на ячейку, где отсутствующие поля представлены пустой строкой ''. Теперь вы можете получить доступ к каждой ячейке или комбинации ячеек, чтобы форматировать их по своему усмотрению. Например, если вы хотите изменить поля в первом столбце от строк до целочисленных значений, вы можете использовать функцию STR2DOUBLE следующим образом

>> data(:,1) = cellfun(@(s) {str2double(s)},data(:,1))

data = 

  Columns 1 through 7

    [  4]    'abc'    'def'    'ghj'    'klm'    ''            ''        
    [NaN]    ''       ''       ''       ''       'Test'        'text'    
    [NaN]    ''       ''       ''       ''       'asdfhsdf'    'dsafdsag'

  Columns 8 through 10

    ''          ''    ''
    '0xFF'      ''    ''
    '0x0F0F'    ''    ''

Обратите внимание, что пустые поля приводят к значениям NaN.

  • 1
    Смотрите также решение @ AndyCampbell ниже для более свежих выпусков Matlab: stackoverflow.com/a/19642332/232610
  • 0
    Хороший. Textscan не любит пустые входы, хотя. Если в отсканированном CSV-файле есть пустые строки, процедура завершается ошибкой. Я бы посоветовал заменить lineArray = lineArray(1:lineIndex-1); по ind = all(cellfun(@isempty,lineArray),2); lineArray = lineArray(~ind);
Показать ещё 1 комментарий
21

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

fid = fopen('file.csv','r');
C = textscan(fid, repmat('%s',1,10), 'delimiter',';', 'CollectOutput',true);
C = C{1};
fclose(fid);

Затем вы можете форматировать столбцы в соответствии с их типом. Например, если первый столбец является целым числом, мы можем отформатировать его как таковой:

C(:,1) = num2cell( str2double(C(:,1)) )

Аналогично, если вы хотите преобразовать 8-й столбец из шестнадцатеричного числа в десятичный, вы можете использовать HEX2DEC:

C(:,8) = cellfun(@hex2dec, strrep(C(:,8),'0x',''), 'UniformOutput',false);

Результирующий массив ячеек выглядит следующим образом:

C = 
    [  4]    'abc'    'def'    'ghj'    'klm'    ''            ''                []    ''    ''
    [NaN]    ''       ''       ''       ''       'Test'        'text'        [ 255]    ''    ''
    [NaN]    ''       ''       ''       ''       'asdfhsdf'    'dsafdsag'    [3855]    ''    ''
14

В R2013b или более поздней версии вы можете использовать таблицу:

>> table = readtable('myfile.txt','Delimiter',';','ReadVariableNames',false)
>> table = 

    Var1    Var2     Var3     Var4     Var5        Var6          Var7         Var8      Var9    Var10
    ____    _____    _____    _____    _____    __________    __________    ________    ____    _____

      4     'abc'    'def'    'ghj'    'klm'    ''            ''            ''          NaN     NaN  
    NaN     ''       ''       ''       ''       'Test'        'text'        '0xFF'      NaN     NaN  
    NaN     ''       ''       ''       ''       'asdfhsdf'    'dsafdsag'    '0x0F0F'    NaN     NaN  

Вот подробнее.

6

Использовать xlsread, он работает так же хорошо. CSV файлы, как и файлы .xls. Укажите, что вы хотите три выхода:

[num char raw] = xlsread('your_filename.csv')

и он даст вам массив, содержащий только числовые данные (num), массив, содержащий только данные символа (char) и массив, который содержит все типы данных в том же формате, что и .csv layout (raw).

6

Вы пытались использовать функцию CSVIMPORT, найденную в обмене файлами? Я сам не пробовал, но он утверждает, что обрабатывает все комбинации текста и цифр.

http://www.mathworks.com/matlabcentral/fileexchange/23573-csvimport

4

В зависимости от формата файла importdata​​strong > может работать.

Вы можете хранить строки в массиве ячеек. Введите "doc cell" для получения дополнительной информации.

  • 0
    Массив ячеек - это то, что нужно OP. OP может даже хранить целочисленные данные там.
  • 0
    Привет, Уильям, «ячейка», кажется, очень хорошая структура данных для моей проблемы. Единственное, с чем я не лажу - это importdata, потому что он не читает в пустых ячейках (из моего CSV) в начале строки ... Я импортирую только «[1x89 char]».
2

Я рекомендую посмотреть массив массивов данных.

Массив массива данных - это тип данных, который поставляется со статистическим инструментом. Он специально предназначен для хранения гемогенных данных в одном контейнере.

Демо-страница Statistics Toolbox содержит пару vidoes, которые показывают некоторые функции массива набора данных. Первый - "Введение в массивы набора данных". Второй - "Введение в союз".

http://www.mathworks.com/products/statistics/demos.html

  • 0
    Спасибо за ваш ответ, примеры выглядят очень полезными, но в моем случае я не установил Statistics Toolbox: /
  • 0
    Смотрите мой ответ ниже, если у вас есть R2013b, это решение будет делать это с таблицей.
1

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

textscan()

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

0
% Assuming that the dataset is ";"-delimited and each line ends with ";"
fid = fopen('sampledata.csv');
tline = fgetl(fid);
u=sprintf('%c',tline); c=length(u);
id=findstr(u,';'); n=length(id);
data=cell(1,n);
for I=1:n
    if I==1
        data{1,I}=u(1:id(I)-1);
    else
        data{1,I}=u(id(I-1)+1:id(I)-1);
    end
end
ct=1;
while ischar(tline)
    ct=ct+1;
    tline = fgetl(fid);
    u=sprintf('%c',tline);
    id=findstr(u,';');
    if~isempty(id)
        for I=1:n
            if I==1
                data{ct,I}=u(1:id(I)-1);
            else
                data{ct,I}=u(id(I-1)+1:id(I)-1);
            end
        end
    end
end
fclose(fid);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню