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.

0
Uri Laserson 23 ago. 2011 a las 09:13

5 respuestas

La mejor respuesta

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.

-1
Wes 23 ago. 2011 a las 06:33

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)))
1
martineau 25 mar. 2013 a las 23:38

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.

0
rocksportrocker 23 ago. 2011 a las 11:53

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()
0
tommy.carstensen 30 sep. 2014 a las 13:44