2016-02-25 20 views
8

Sto usando openpyxl in python e sto provando a eseguire 50k linee e prendere i dati da ogni riga e inserirli in un file. Comunque ... quello che sto scoprendo è che corre incredibilmente lento più lontano ci arrivo. Le prime linee da 1k diventano super veloci, meno di un minuto, ma dopo ci vuole più tempo, sempre più tempo per fare le successive linee da 1k.Il modo più veloce per eseguire file da 50k di file Excel in OpenPYXL

Stavo aprendo un file .xlsx. Mi chiedo se è più veloce aprire un file .txt come csv o qualcosa del genere o leggere un file json o qualcosa del genere? O convertire in qualche modo in qualcosa che leggerà più velocemente?

Ho 20 valori univoci in una data colonna e quindi i valori sono casuali per ogni valore. Sto cercando di afferrare una stringa dell'intera colonna del valore univoco per ogni valore.

Value1: 1243,345,34,124, Valore2: 1243,345,34,124, ecc, ecc

sto correndo attraverso l'elenco valore, vedere se esiste il nome di un file, se lo fa , quindi accederà a quel file e aggiungerà ad esso il nuovo valore, se il file non esiste, creerà il file e quindi lo imposterà. Ho un dizionario che ha tutte le cose "aggiungi file di scrittura" ad esso collegate, quindi ogni volta che voglio scrivere qualcosa, prenderà il nome del file, e la cosa di append sarà disponibile nel dict, la cercherà e scrivi su quel file, quindi non continua ad aprire nuovi file ogni volta che viene eseguito.

Il primo 1k ha impiegato meno di un minuto .. ora sono su dischi da 4k a 5k, ed è in esecuzione tutti i 5 minuti pronti .. sembra che impieghi più tempo mentre sale nei record, mi chiedo come accelerare su. Non sta affatto stampando sulla console.

writeFile = 1 
theDict = {} 

for row in ws.iter_rows(rowRange): 
    for cell in row: 
     #grabbing the value 
     theStringValueLocation = "B" + str(counter) 
     theValue = ws[theStringValueLocation].value 
     theName = cell.value 
     textfilename = theName + ".txt" 

     if os.path.isfile(textfilename): 
      listToAddTo = theDict[theName] 
      listToAddTo.write("," + theValue) 
      if counter == 1000: 
       print "1000" 
       st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') 

     else: 
      writeFileName = open(textfilename, 'w') 
      writeFileName.write(theValue) 
      writeFileName = open(textfilename, 'a') 
      theDict[theName] = writeFileName 
     counter = counter + 1 

ho aggiunto alcuni francobolli di tempo per il codice di cui sopra, non è lì, ma si può vedere l'output di seguito. Il problema che sto vedendo è che sta salendo sempre più in alto ogni 1k di corsa. 2 minuti la prima volta, 3 minuti, poi 5 minuti, poi 7 minuti. Quando arriva a 50k, sono preoccupato che ci vorrà un'ora o qualcosa e ci vorrà troppo tempo.

1000 
2016-02-25 15:15:08 
20002016-02-25 15:17:07 
30002016-02-25 15:20:52 
2016-02-25 15:25:28 
4000 
2016-02-25 15:32:00 
5000 
2016-02-25 15:40:02 
6000 
2016-02-25 15:51:34 
7000 
2016-02-25 16:03:29 
8000 
2016-02-25 16:18:52 
9000 
2016-02-25 16:35:30 
10000 

Qualcosa che dovrei mettere in chiaro .. Io non so i nomi dei valori prima del tempo, forse dovrei correre attraverso e prendere quelli in uno script python separata per rendere questo andare più veloce?

In secondo luogo, ho bisogno di una stringa di tutti i valori separati da virgola, è per questo che l'ho messo in un file di testo per afferrare in seguito. Stavo pensando di farlo da una lista come mi è stato suggerito, ma mi chiedo se avrà lo stesso problema. Sto pensando che il problema abbia a che fare con la lettura di Excel. Ad ogni modo posso ottenere una stringa separata da una virgola, posso farlo in un altro modo.

O forse potrei fare try/catch invece di cercare il file ogni volta, e se c'è un errore, posso assumere per creare un nuovo file? Forse la ricerca ogni volta sta facendo andare davvero lento? il Se il file esiste?

questa domanda è una continuazione del mio originale qui ed io abbiamo preso alcuni suggerimenti da lì .... What is the fastest performance tuple for large data sets in python?

+0

L'indentazione del codice è sbagliato, che significa che non possiamo sapere come sono strutturati i tuoi anelli. Perché stai ripetendo le celle e poi accedendo a una cella nella colonna B di una riga? Immagino che 'ws [" B "+ str (counter)]. Value' sia eseguito in tempo lineare. 'contatore', non in tempo costante. – roeland

+0

no il rientro non è sbagliato. hai capito ... sto prendendo due valori. quello B è dove si trova il "valore". il "cell.value" è il nome. sto usando il contatore per afferrare quello sulla stessa fila l'uno dell'altro. – king

+0

oh sì, hai ragione .. ci dovrebbe essere un rientro dopo per cella in fila .. lo aggiusterò – king

risposta

2

Sembra desideri solo cellule dal B-colonna. In questo caso è possibile utilizzare ws.get_squared_range() per limitare il numero di celle da esaminare.

for row in ws.get_squared_range(min_col=2, max_col=2, min_row=1, max_row=ws.max_row): 
    for cell in row: # each row is always a sequence 
     filename = cell.value 
     if os.path.isfilename(filename): 
       … 

Non è chiaro quello che sta succedendo con il ramo else del codice, ma si dovrebbe probabilmente essere a chiudere i file aperti non appena avete finito con loro.

+0

il ramo else è se il il file non esiste, crea il file e quindi lo lascia aperto in modo che possa continuare ad aggiungere " – king

+0

", alla fine di ogni file, è così che non deve continuare a riaprire il file ogni volta, lasciandoli semplicemente aperti , ci sono circa 20 file che si apriranno – king

+1

Invece di fornire uno script incompleto qui con data e ora, ti suggerisco di usare il modulo profilo dalla libreria standard per scoprire dove il tuo codice sta spendendo la maggior parte del suo tempo. –

3

Penso che quello che stai cercando di fare è estrarre una chiave dalla colonna B della riga e usarla per il nome del file da aggiungere. Diciamo accelerarlo un sacco:

from collections import defaultdict 
Value_entries = defaultdict(list) # dict of lists of row data 

for row in ws.iter_rows(rowRange): 
    key = row[1].value 

    Value_entries[key].extend([cell.value for cell in row]) 

# All done. Now write files: 
for key in Value_entries.keys(): 
    with open(key + '.txt', 'w') as f: 
     f.write(','.join(Value_entries[key])) 
+0

Non è necessario calcolare le coordinate di stile A1. 'ws.cell (...)' accetta valori numerici (1) per righe e colonne. Ma non è necessario né all'interno di iter_rows, poiché puoi contare sull'enumerazione o sull'indicizzazione semplice: le celle da "B" sarebbero riga [1], assumendo che l'intervallo inizi da "A". –

+0

key = row [1] .value IndexError: index tuple out of range – king

+0

hai ragione .. sto tirando per tirare fuori un valore da B .. vedo che lo stai facendo con row [1] .value. anche se non sono sicuro del motivo per cui sta portando un errore – king

2

Sulla base l'altra domanda si è collegato al, e il codice di cui sopra, sembra si dispone di un foglio di calcolo di nome - coppie di valori. Il nome inserito nella colonna A e il valore è nella colonna B. Un nome può apparire più volte nella colonna A e può esserci un valore diverso nella colonna B ogni volta. L'obiettivo è creare un elenco di tutti i valori visualizzati per ciascun nome.

In primo luogo, alcune osservazioni sul codice di cui sopra:

  1. counter viene mai inizializzato. Presumibilmente è inizializzato a 1.

  2. open(textfilename,...) viene chiamato due volte senza chiudere il file in mezzo. La chiamata aperta alloca un po 'di memoria per contenere i dati relativi al funzionamento del file. La memoria allocata per la prima chiamata aperta potrebbe non essere liberata fino a molto più tardi, forse non fino alla fine del programma. È preferibile chiudere i file quando hai finito con loro (vedi l'utilizzo di open come gestore di contesto).

  3. La logica di loop non è corretta. Considerare:

prima iterazione del ciclo interno:

for cell in row:      # cell refers to A1 
    valueLocation = "B" + str(counter) # valueLocation is "B1" 
    value = ws[valueLocation].value  # value gets contents of cell B1 
    name = cell.value     # name gets contents of cell A1 
    textfilename = name + ".txt" 
    ... 
    opens file with name based on contents of cell A1, and 
    writes value from cell B1 to the file 
    ... 
    counter = counter + 1      # counter = 2 

Ma ogni riga ha almeno due celle, così sulla seconda iterazione del ciclo interno:

for cell in row:       # cell now refers to cell B1 
    valueLocation = "B" + str(counter) # valueLocation is "B2" 
    value = ws[valueLocation].value  # value gets contents of cell B2 
    name = cell.value      # name gets contents of cell B1 
    textfilename = name + ".txt" 
    ... 
    opens file with name based on contents of cell "B1" <<<< wrong file 
    writes the value of cell "B2" to the file   <<<< wrong value 
    ... 
    counter = counter + 1  # counter = 3 when cell B1 is processed 

Ripetere per ognuna delle 50 righe. A seconda del numero di valori univoci presenti nella colonna B, il programma potrebbe provare ad avere centinaia o migliaia di file aperti (basati sul contenuto delle celle A1, B1, A2, B2, ...) == >> molto lento o programma si blocca.

  1. iter_rows() restituisce una tupla delle celle nella riga.

  2. Come suggeriscono le persone nell'altra domanda, utilizzare un dizionario e elenchi per archiviare i valori e scriverli tutti alla fine. In questo modo (Im usando python 3.5, quindi potrebbe essere necessario modificare questo se si utilizza 2,7)

Ecco una soluzione dritto in avanti:

from collections import defaultdict 

data = defaultdict(list) 

# gather the values into lists associated with each name 
# data will look like { 'name1':['value1', 'value42', ...], 
#      'name2':['value7', 'value23', ...], 
#      ...} 
for row in ws.iter_rows(): 
    name = row[0].value 
    value = row[1].value 
    data[name].append(value) 

for key,valuelist in data.items(): 
    # turn list of strings in to a long comma-separated string 
    # e.g., ['value1', 'value42', ...] => 'value1,value42, ...' 
    value = ",".join(valuelist) 

    with open(key + ".txt", "w") as f: 
     f.write(value) 
+0

ciao scusa per aver mancato il codice sulla mia fine, bello scrivere, proverò un po 'di questo ora. ma una osservazione mentre sto lavorando verso il basso, il valore della cella, ho impostato un intervallo per tutti i valori sopra questo che non ho postato. quindi ho impostato un intervallo di D1: D50000 in basso. quindi il valore della cella colpisce costantemente ogni valore di cella, e quindi il valore B viene generato automaticamente ogni volta in base al contatore – king

+0

perché utilizzare Append anziché extend? – king

+0

'append' aggiunge un singolo oggetto alla fine di una lista. 'estendi' viene utilizzato quando si aggiungono più elementi (come un elenco di elementi). Qui stiamo aggiungendo il valore singolo, quindi 'append' è appropriato. – RootTwo