2011-01-20 10 views
35

Estoy trabajando con MATLAB durante unos días y estoy teniendo dificultades para importar un archivo CSV a una matriz.Importar archivo CSV con tipos de datos mixtos

Mi problema es que mi archivo CSV contiene casi solo cadenas y algunos valores enteros, por lo que csvread() no funciona. csvread() solo se lleva bien con valores enteros.

¿Cómo puedo almacenar mis cadenas en una especie de matriz bidimensional para tener acceso libre a cada elemento?

Aquí está un CSV de ejemplo para mis necesidades:

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

Lo principal son las celdas vacías y los textos dentro de las células. Como ve, la estructura puede variar.

+3

Eche un vistazo a textscan> http://www.mathworks.com/help/techdoc/ref/textscan.html – zellus

+2

debe publicar un par de filas de su archivo CSV para que tengamos una idea de cómo es el archivo estructurado (son las cadenas citadas con ''' o '" ', contienen comas dentro de las comillas, también se citan los valores enteros, ¿hay algún delimitador escapado como' "esto es \" un ejemplo \ "" ' etc ...) – Amro

+0

Hecho :) Espero que ayude un poquito – poeschlorn

Respuesta

51

Editado ahora que la cuestión se ha actualizado con un archivo de entrada de ejemplo ...

Para el caso cuando se sabe cuántas columnas de datos que habrá en el archivo CSV, una simple llamada a TEXTSCAN como Amro suggests será su mejor solución.

Sin embargo, si no conoce a priori cuántas columnas hay en su archivo, puede usar un enfoque más general como lo hice en la siguiente función. Primero utilicé la función FGETL para leer cada línea del archivo en una matriz de celdas. Luego usé la función TEXTSCAN para analizar cada línea en cadenas separadas usando un delimitador de campo predefinido y tratando los campos enteros como cadenas por ahora (se pueden convertir a valores numéricos más adelante).Aquí está el código resultante, se coloca en una función 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 

La ejecución de esta función en el contenido del archivo de muestra de la pregunta da este resultado:

>> 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' '' '' 

El resultado es un 3-por-10 matriz de celdas con un campo por celda donde los campos faltantes están representados por la cadena vacía ''. Ahora puede acceder a cada celda o a una combinación de celdas para formatearlas a su gusto. Por ejemplo, si usted quiere cambiar los campos en la primera columna de cadenas en valores enteros, se puede utilizar la función STR2DOUBLE de la siguiente manera:

>> 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' '' '' 

cuenta que los campos vacíos resultados en NaN valores.

+1

Consulte también la solución de @ AndyCampbell a continuación para versiones más recientes de Matlab: http: // stackoverflow.com/a/19642332/232610 – Jonas

+0

Agradable. Sin embargo, a Textscan no le gustan las entradas vacías. Si el archivo csv escaneado tiene filas vacías, el procedimiento falla. Yo aconsejaría sustituir 'LineArray = LineArray (1: lineIndex-1);' por 'ind = all (cellfun (@ IsEmpty, LineArray), 2); ' ' lineArray = lineArray (~ ind); ' – Alex

+0

También puede usar' strsplit() 'con el parámetro' CollapseDelimiters' establecido en 'false' para tokenizar cada línea del archivo csv. Matlab, por defecto, colapsa los delimitadores consecutivos. Establecido en falso, el resultado es el siguiente: '{'' '' '' '' '' 'Test' 'text' '0xFF' '' ''}' para el comando 'strsplit (';;;;; Test; texto; 0xFF ;; ','; ',' CollapseDelimiters ', falso) ' –

4

Dependiendo del formato de su archivo, importdata podría funcionar.

Puede almacenar cadenas en una matriz de celdas. Escriba "celda de documento" para obtener más información.

+0

Una matriz de celdas es lo que OP necesita. OP incluso podría almacenar los datos enteros allí. – Marm0t

+0

Hola William, "célula" parece ser una muy buena estructura de datos para mi problema. Lo único con lo que no me llevo bien es con "importdata", porque no lee en celdas vacías (desde mi CSV) al comienzo de una línea ... Solo me da un "[1x89 carbonero]" importado – poeschlorn

2

Recomiendo mirar la matriz de conjuntos de datos.

La matriz del conjunto de datos es un tipo de datos que se envía con Statistics Toolbox. Está específicamente diseñado para almacenar datos heterogéneos en un único contenedor.

La página de demostración de Statistics Toolbox contiene un par de vides que muestran algunas de las características del conjunto de datos. El primero se titula "Una introducción a las matrices de conjuntos de datos". El segundo se titula "Una introducción a las uniones".

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

+0

Gracias por su a nswer, los ejemplos parecen muy útiles, pero en mi caso no tengo Statistics Toolbox instalado:/ – poeschlorn

+0

Consulte mi respuesta a continuación, si tiene R2013b esta solución se convierte en esto con una tabla. –

1

Si el archivo de entrada tiene una cantidad fija de columnas separadas por comas y sabes en que las columnas son las cuerdas que podría ser la mejor manera de utilizar la función

textscan() 

en cuenta que puede especifique un formato donde lea hasta un número máximo de caracteres en la cadena o hasta que se encuentre un delimitador (coma).

20

dada la muestra informados, este código simple debe hacer el trabajo:

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

entonces se podría dar formato a las columnas según su tipo. Por ejemplo, si la primera columna es todos los números enteros, podemos darle formato como tal:

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

mismo modo, si se desea convertir la octava columna de hex a decimales, puede utilizar HEX2DEC:

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

La matriz de células resultante se ve de la siguiente manera:

C = 
    [ 4] 'abc' 'def' 'ghj' 'klm' ''   ''    [] '' '' 
    [NaN] ''  ''  ''  ''  'Test'  'text'  [ 255] '' '' 
    [NaN] ''  ''  ''  ''  'asdfhsdf' 'dsafdsag' [3855] '' '' 
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); 
6

uso xlsread, funciona igual de bien en csv como lo hace en archivos .xls. Especifica que desea tres salidas:

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

y se le dará una matriz que contiene solamente los datos numéricos (NUM), una matriz que contiene solamente los datos de caracteres (CHAR) y una matriz que contiene todos los tipos de datos en el mismo formato que el diseño .csv (sin formato).

14

En R2013b o temprano se puede utilizar una tabla:

>> 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 

Aquí es more info.

Cuestiones relacionadas