plano - leer archivos txt en matlab
La lectura más rápida de archivos Matlab? (4)
Planteamiento del problema
Esta es una lucha común, y no hay nada como una prueba para responder. Aquí están mis suposiciones:
Un archivo ASCII bien formateado, que contiene dos columnas de números. Sin encabezados, líneas inconsistentes, etc.
El método debe escalar para leer archivos que son demasiado grandes para contenerlos en la memoria (aunque mi paciencia es limitada, entonces mi archivo de prueba solo tiene 500,000 líneas).
La operación real (lo que el OP llama "hacer cosas con nums") debe realizarse una fila a la vez, no puede vectorizarse.
Discusión
Teniendo esto en cuenta, las respuestas y los comentarios parecen alentar la eficiencia en tres áreas:
- leyendo el archivo en lotes más grandes
- realizar la cadena de conversión de números de manera más eficiente (ya sea por lotes o utilizando mejores funciones)
- haciendo el procesamiento real más eficiente (que he descartado por la regla 3 anterior).
Resultados
Arreglé un guión rápido para probar la velocidad de ingestión (y la consistencia del resultado) de 6 variaciones en estos temas. Los resultados son:
- Código inicial 68.23 seg . 582582 cheque
- Usando sscanf, una vez por línea. 27.20 seg. 582582 cheque
- Usando fscanf en lotes grandes. 8.93 seg . 582582 cheque
- El uso de textos puede buscar en grandes lotes. 8.79 seg . 582582 cheque
- Lectura de grandes lotes en la memoria, luego sscanf. 8.15 seg. 582582 cheque
- Usando el lector de archivos de una sola línea java y sscanf en líneas simples. 63.56 seg. 582582 cheque
- Usando el escáner de token de un solo elemento de Java. 81.19 seg. 582582 cheque
- Operaciones totalmente agrupadas (no conforme). 1.02 seg. Cheque 508680 (viola la regla 3)
Resumen
Más de la mitad del tiempo original (68 -> 27 segundos) se consumió con ineficiencias en la llamada str2num, que se puede eliminar al cambiar el sscanf.
Aproximadamente otros 2/3 del tiempo restante (27 -> 8 segundos) se pueden reducir usando lotes más grandes para la lectura de archivos y la conversión de cadenas a números.
Si estamos dispuestos a violar la regla número tres en la publicación original, se puede reducir otro 7/8 del tiempo cambiando a un procesamiento totalmente numérico. Sin embargo, algunos algoritmos no se prestan a esto, así que lo dejamos en paz. (No el valor de "verificación" no coincide con la última entrada).
Finalmente, en contradicción directa con una edición previa mía dentro de esta respuesta, no hay ahorros disponibles cambiando los lectores de línea única Java disponibles en caché. De hecho, esa solución es 2 - 3 veces más lenta que el resultado de una sola línea comparable utilizando lectores nativos. (63 vs. 27 segundos).
El código de muestra para todas las soluciones descritas anteriormente se incluye a continuación.
Código de muestra
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create a test file
cd(tempdir);
fName = ''demo_file.txt'';
fid = fopen(fName,''w'');
for ixLoop = 1:5
d = randi(1e6, 1e5,2);
fprintf(fid, ''%d, %d /n'',d);
end
fclose(fid);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Initial code
CHECK = 0;
tic;
fid = fopen(''demo_file.txt'');
tline = fgetl(fid);
while ischar(tline)
nums = str2num(tline);
CHECK = round((CHECK + mean(nums) ) /2);
tline = fgetl(fid);
end
fclose(fid);
t = toc;
fprintf(1,''Initial code. %3.2f sec. %d check /n'', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using sscanf, once per line
CHECK = 0;
tic;
fid = fopen(''demo_file.txt'');
tline = fgetl(fid);
while ischar(tline)
nums = sscanf(tline,''%d, %d'');
CHECK = round((CHECK + mean(nums) ) /2);
tline = fgetl(fid);
end
fclose(fid);
t = toc;
fprintf(1,''Using sscanf, once per line. %3.2f sec. %d check /n'', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using fscanf in large batches
CHECK = 0;
tic;
bufferSize = 1e4;
fid = fopen(''demo_file.txt'');
scannedData = reshape(fscanf(fid, ''%d, %d'', bufferSize),2,[])'' ;
while ~isempty(scannedData)
for ix = 1:size(scannedData,1)
nums = scannedData(ix,:);
CHECK = round((CHECK + mean(nums) ) /2);
end
scannedData = reshape(fscanf(fid, ''%d, %d'', bufferSize),2,[])'' ;
end
fclose(fid);
t = toc;
fprintf(1,''Using fscanf in large batches. %3.2f sec. %d check /n'', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using textscan in large batches
CHECK = 0;
tic;
bufferSize = 1e4;
fid = fopen(''demo_file.txt'');
scannedData = textscan(fid, ''%d, %d /n'', bufferSize) ;
while ~isempty(scannedData{1})
for ix = 1:size(scannedData{1},1)
nums = [scannedData{1}(ix) scannedData{2}(ix)];
CHECK = round((CHECK + mean(nums) ) /2);
end
scannedData = textscan(fid, ''%d, %d /n'', bufferSize) ;
end
fclose(fid);
t = toc;
fprintf(1,''Using textscan in large batches. %3.2f sec. %d check /n'', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Reading in large batches into memory, incrementing to end-of-line, sscanf
CHECK = 0;
tic;
fid = fopen(''demo_file.txt'');
bufferSize = 1e4;
eol = sprintf(''/n'');
dataBatch = fread(fid,bufferSize,''uint8=>char'')'';
dataIncrement = fread(fid,1,''uint8=>char'');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,''uint8=>char''); %This can be slightly optimized
end
data = [dataBatch dataIncrement];
while ~isempty(data)
scannedData = reshape(sscanf(data,''%d, %d''),2,[])'';
for ix = 1:size(scannedData,1)
nums = scannedData(ix,:);
CHECK = round((CHECK + mean(nums) ) /2);
end
dataBatch = fread(fid,bufferSize,''uint8=>char'')'';
dataIncrement = fread(fid,1,''uint8=>char'');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,''uint8=>char'');%This can be slightly optimized
end
data = [dataBatch dataIncrement];
end
fclose(fid);
t = toc;
fprintf(1,''Reading large batches into memory, then sscanf. %3.2f sec. %d check /n'', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using Java single line readers + sscanf
CHECK = 0;
tic;
bufferSize = 1e4;
reader = java.io.LineNumberReader(java.io.FileReader(''demo_file.txt''),bufferSize );
tline = char(reader.readLine());
while ~isempty(tline)
nums = sscanf(tline,''%d, %d'');
CHECK = round((CHECK + mean(nums) ) /2);
tline = char(reader.readLine());
end
reader.close();
t = toc;
fprintf(1,''Using java single line file reader and sscanf on single lines. %3.2f sec. %d check /n'', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using Java scanner for file reading and string conversion
CHECK = 0;
tic;
jFile = java.io.File(''demo_file.txt'');
scanner = java.util.Scanner(jFile);
scanner.useDelimiter(''[/s/,/n/r]+'');
while scanner.hasNextInt()
nums = [scanner.nextInt() scanner.nextInt()];
CHECK = round((CHECK + mean(nums) ) /2);
end
scanner.close();
t = toc;
fprintf(1,''Using java single item token scanner. %3.2f sec. %d check /n'', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Reading in large batches into memory, vectorized operations (non-compliant solution)
CHECK = 0;
tic;
fid = fopen(''demo_file.txt'');
bufferSize = 1e4;
eol = sprintf(''/n'');
dataBatch = fread(fid,bufferSize,''uint8=>char'')'';
dataIncrement = fread(fid,1,''uint8=>char'');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,''uint8=>char''); %This can be slightly optimized
end
data = [dataBatch dataIncrement];
while ~isempty(data)
scannedData = reshape(sscanf(data,''%d, %d''),2,[])'';
CHECK = round((CHECK + mean(scannedData(:)) ) /2);
dataBatch = fread(fid,bufferSize,''uint8=>char'')'';
dataIncrement = fread(fid,1,''uint8=>char'');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,''uint8=>char'');%This can be slightly optimized
end
data = [dataBatch dataIncrement];
end
fclose(fid);
t = toc;
fprintf(1,''Fully batched operations. %3.2f sec. %d check /n'', t, CHECK);
(respuesta original)
Para ampliar el punto hecho por Ben ... su cuello de botella siempre será archivo E / S si está leyendo estos archivos línea por línea.
Entiendo que a veces no se puede guardar todo un archivo en la memoria. Normalmente leo en un gran lote de caracteres (1e5, 1e6 o más, dependiendo de la memoria de su sistema). Luego, o leo caracteres individuales adicionales (o retrocedo en caracteres individuales) para obtener un número redondo de líneas, y luego ejecuto el análisis de cadenas (por ejemplo, sscanf).
Entonces, si lo desea, puede procesar la gran matriz resultante una fila a la vez, antes de repetir el proceso hasta que lea el final del archivo.
Es un poco tedioso, pero no tan difícil. Normalmente veo un 90% más de mejora en la velocidad sobre los lectores de línea única.
(idea terrible usando lectores de línea batida de Java eliminados con vergüenza)
Mi programa MATLAB está leyendo un archivo de aproximadamente 7m de longitud y está perdiendo demasiado tiempo en I / O. Sé que cada línea tiene el formato de dos enteros, pero no sé exactamente cuántos caracteres ocupan. str2num es mortalmente lento, ¿qué función de matlab debo usar en su lugar?
Captura: tengo que operar en cada línea de a una por vez sin almacenar toda la memoria de archivos, por lo que ninguno de los comandos que leen matrices completas está sobre la mesa.
fid = fopen(''file.txt'');
tline = fgetl(fid);
while ischar(tline)
nums = str2num(tline);
%do stuff with nums
tline = fgetl(fid);
end
fclose(fid);
Descubrí que MATLAB lee archivos csv significativamente más rápido que los archivos de texto, por lo que si es posible convertir el archivo de texto a csv utilizando algún otro software, esto puede acelerar significativamente las operaciones de Matlab.
He tenido buenos resultados (velocidad) usando memmapfile()
. Esto minimiza la cantidad de copia de datos de memoria, y hace uso del buffer de IO del kernel. Necesita suficiente espacio libre de direcciones (aunque no memoria real) para mapear todo el archivo, y suficiente memoria libre para mantener la variable de salida (¡obviamente!)
El siguiente código de ejemplo lee un archivo de texto en una matriz de data
de dos columnas de tipo int32.
fname = ''file.txt'';
fstats = dir(fname);
% Map the file as one long character string
m = memmapfile(fname, ''Format'', {''uint8'' [ 1 fstats.bytes] ''asUint8''});
textdata = char(m.Data(1).asUint8);
% Use textscan() to parse the string and convert to an int32 matrix
data = textscan(textdata, ''%d %d'', ''CollectOutput'', 1);
data = data{:};
% Tidy up!
clear(''m'')
Es posible que necesite textscan()
los parámetros de textscan()
para obtener exactamente lo que desea: consulte los documentos en línea.
Incluso si no puede colocar todo el archivo en la memoria, debería leer un lote grande usando las funciones de lectura matricial.
Tal vez incluso puedas usar operaciones vectoriales para algunos de los procesos de datos, lo que aceleraría las cosas.