Para una de mis líneas de análisis de datos, termino generando muchos archivos CSV individuales. Me gustaría transponerlos, concatenarlos y transponerlos nuevamente. Sin embargo, la cantidad de datos es grande, por lo que cargar todo en la memoria no es práctico.
5 respuestas
Use un generador, p.
from itertools import izip
file1 = open("test", "r")
file2 = open("test2", "r")
def lazy(file):
for line in file:
#do something with the line
yield line
for lines in izip(lazy(file1), lazy(file2)):
print lines
http://wiki.python.org/moin/Generators
Editar: puede usar el módulo CSV para analizarlo, también me di cuenta de que el método readlines () de los objetos de archivo no es perezoso, por lo que debe usar el patrón for line in file.
Concatenar las filas de datos de dos archivos csv (si eso es lo que quiso decir) sin cargarlos a ambos en la memoria es una operación relativamente fácil y rápida: solo lea en una sola fila de cada uno, únalos y luego escriba eso a un archivo de salida, repitiendo hasta que se agoten todos los datos de entrada.
La transposición de los datos en un archivo csv es sin leer todo en la memoria intrínsecamente va a ser un proceso mucho más lento, ya que requiere que todo el archivo de entrada se vuelva a leer en varias pasadas, cada vez que se extraen los datos de una sola columna que contiene . Si esa es una compensación aceptable (o necesaria), aquí es básicamente cómo se haría utilizando el módulo incorporado csv
:
import csv
input_filename = 'input.csv'
output_filename = 'output.csv'
with open(output_filename, 'wb') as outputf:
writer = csv.writer(outputf)
with open(input_filename, 'rb') as inputf:
# determine number of columns in input file by counting those in its first row
# number of cols in input file determines number of rows in output file
numcols = len(csv.reader(inputf).next())
# read entire input file multiple times, extracting one column from each row
for col_index in xrange(numcols):
# write all of column data as a single row of the output file
inputf.seek(0) # rewind file for each pass
writer.writerow(tuple(row[col_index] for row in csv.reader(inputf)))
Otra solución corta y pitónica. Usé esto para transponer CSV de 15,000,000 x 12,000. Es un pitón rápido y puro. Todo lo demás que necesitas hacer es trivial y esta es definitivamente la parte más difícil.
Enlace de Github: https://gist.github.com/arose13/facfb91b609d453f3ad840417faa503
def transpose_csv_out_of_core(csv_path, output_csv_path='transposed.csv', delimiter=','):
"""
On my laptop it can transpose at ~375,000 lines a sec
:param csv_path:
:param output_csv_path:
:param delimiter:
:return:
"""
import csv
transposed_iterator = zip(*csv.reader(open(csv_path)))
with open(output_csv_path, 'w') as out:
for row in transposed_iterator:
out.write(delimiter.join(row) + '\n')
El siguiente código simula la lectura de dos archivos csv. El primero tiene las dos filas.
[1,2,1]
[3,4,1]
El segundo
[7,8,2]
[9,10.2].
El resultado son las dos filas.
[1,2,1,7,8,2]
[3,4,1,9,10,2]
Es eso lo que querías ?
def source1():
for i in [ [1,2, 1] ,[3,4, 1]] : yield i
def source2():
for i in [ [7,8,2] ,[9,10,2]] : yield i
def join(*sources):
while True:
row = []
for s in sources:
row.extend(s.next())
yield row
for row in join(source1(), source2()):
print row
En su caso, debe reemplazar las llamadas a source1 () y source2 () por los iteradores de archivos csv.
Aquí hay una solución que funciona cuando los campos tienen anchos fijos:
import sys
import os
def main():
path_in = sys.argv[-1]
path_out = os.path.basename(path_in)+'.transposed'
with open(path_in) as fd_in:
line = fd_in.readline()
l = line.split()
field_width = int(len(line)/len(l))
file_size = os.path.getsize(path_in)
cols2 = rows1 = line_count = int(file_size/len(line))
rows2 = cols1 = len(l)
with open(path_in) as fd_in, open(path_out, 'w') as fd_out:
for row in range(rows2):
for col in range(cols2-1):
fd_in.seek(col*len(line)+row*field_width)
fd_out.write('{} '.format(fd_in.read(field_width-1)))
fd_in.seek((col+1)*len(line)+row*field_width)
fd_out.write('{}\n'.format(fd_in.read(field_width-1)))
return
if __name__ == '__main__':
main()
Aquí hay una solución que funciona si los campos no tienen anchos fijos:
import sys
import os
def main():
path_in = sys.argv[-1]
path_out = os.path.basename(path_in)+'.transposed'
separator = ' '
d_seek = {}
with open(path_in) as fd_in:
i = 0
while True:
tell = fd_in.tell()
if fd_in.readline() == '':
break
d_seek[i] = tell
i += 1
cols2 = rows1 = i
with open(path_in) as fd_in:
line = fd_in.readline()
rows2 = cols1 = len(line.split(separator))
del line
with open(path_in) as fd_in, open(path_out, 'w') as fd_out:
for row2 in range(rows2):
for row1 in range(rows1):
fd_in.seek(d_seek[row1])
j = 0
s = ''
while True:
char = fd_in.read(1)
j += 1
if char == separator or char == '\n':
break
s += char
d_seek[row1] += len(s)+1
if row1+1 < rows1:
fd_out.write('{} '.format(s))
else:
fd_out.write('{}\n'.format(s))
return
if __name__ == '__main__':
main()
Preguntas relacionadas
Preguntas vinculadas
Nuevas preguntas
python
Python es un lenguaje de programación multipropósito, de tipificación dinámica y de múltiples paradigmas. Está diseñado para ser rápido de aprender, comprender y usar, y hacer cumplir una sintaxis limpia y uniforme. Tenga en cuenta que Python 2 está oficialmente fuera de soporte a partir del 01-01-2020. Aún así, para preguntas de Python específicas de la versión, agregue la etiqueta [python-2.7] o [python-3.x]. Cuando utilice una variante de Python (por ejemplo, Jython, PyPy) o una biblioteca (por ejemplo, Pandas y NumPy), inclúyala en las etiquetas.