2015-07-03 14 views
5

Sono abbastanza nuovo in StackOverflow e di recente ho imparato alcune basi Python. Questa è la prima volta che sto usando openpyxl. Prima ho usato xlrd e xlsxwriter e sono riuscito a creare alcuni programmi utili. Ma in questo momento ho bisogno di uno scrittore .xlsx &.Python Trova la riga più alta in una determinata colonna

C'è un file che ho bisogno di leggere e modificare con i dati già memorizzati nel codice. Supponiamo che il .xlsx abbia cinque colonne con i dati: A, B, C, D, E. Nella colonna A, ho più di 1000 righe con i dati. Sulla colonna D, ho 150 righe con dati.

Fondamentalmente, voglio che il programma trovi l'ultima riga con i dati su una determinata colonna (diciamo D). Quindi, scrivere la variabile memorizzata data nella riga successiva disponibile (ultima riga + 1) nella colonna D.

Il problema è che non posso usare ws.get_highest_row() perché restituisce la riga 1000 su colonna A.

Fondamentalmente, finora questo è tutto quello che ho:

data = 'xxx' 
from openpyxl import load_workbook 
wb = load_workbook('book.xlsx', use_iterators=True) 
ws = wb.get_sheet_by_name('Sheet1') 
last_row = ws.get_highest_row() 

Ovviamente questo non funziona affatto. last_row restituisce 1000.

+1

Dovete usare 'openpyxl' o si potrebbe utilizzare un'altra libreria? Sembra che potrebbe essere un lavoro per 'pandas', la libreria di elaborazione dei dati di Python. – LondonRob

+0

Qualsiasi cosa sarebbe perfetta ma il file che sto lavorando è un xlsx. Ho letto che openpyxl è l'unico che legge e scrive. – egodial

risposta

1

Ecco come utilizzare Pandas.

It's easy per ottenere l'ultima riga non null in Pandas utilizzando last_valid_index.

Ci potrebbe essere un modo migliore di scrivere la risultante DataFrame al file xlsx ma, according to the docs, in questo modo molto stupido è in realtà come si fa in openpyxl.

Diciamo che si sta iniziando con questo semplice foglio di lavoro:

Original worksheet

Diciamo che vogliamo mettere xxx nella colonna C:

import openpyxl as xl 
import pandas as pd 

wb = xl.load_workbook('deleteme.xlsx') 
ws = wb.get_sheet_by_name('Sheet1') 
df = pd.read_excel('deleteme.xlsx') 

def replace_first_null(df, col_name, value): 
    """ 
    Replace the first null value in DataFrame df.`col_name` 
    with `value`. 
    """ 
    return_df = df.copy() 
    idx = list(df.index) 
    last_valid = df[col_name].last_valid_index() 
    last_valid_row_number = idx.index(last_valid) 
    # This next line has mixed number and string indexing 
    # but it should be ok, since df is coming from an 
    # Excel sheet and should have a consecutive index 
    return_df.loc[last_valid_row_number + 1, col_name] = value 
    return return_df 

def write_df_to_worksheet(ws, df): 
    """ 
    Write the values in df to the worksheet ws in place 
    """ 
    for i, col in enumerate(replaced): 
     for j, val in enumerate(replaced[col]): 
      if not pd.isnull(val): 
       # Python is zero indexed, so add one 
       # (plus an extra one to take account 
       # of the header row!) 
       ws.cell(row=j + 2, column=i + 1).value = val 

# Here's the actual replacing happening 
replaced = replace_first_null(df, 'C', 'xxx') 
write_df_to_worksheet(ws, df) 
wb.save('changed.xlsx') 

che si traduce in:

Edited Excel file

+0

Questo codice è fantastico, @LondonRob!. Ha un comportamento un po 'strano con .xlsx, ad es. nasconde le colonne (molte di esse). Perché? Inoltre, sto cercando di modificare due cose, ma dovrò studiare ulteriormente questo codice per farlo: i) Ho bisogno del codice per trovare l'ultimo valore in C, scrivere 'xxx' e, ad esempio, scrivere sullo stesso fila nelle prossime due colonne 'yyy' e 'zzz' ii) per quanto posso vedere, dipende troppo dall'intestazione delle colonne. È ottimale se funziona con la posizione della colonna o un indice (questo sembra abbastanza difficile da fare). – egodial

+0

Felice di aiutare! Mi sembrano nuove domande. Otterrai il miglior aiuto pubblicandoli separatamente per questa domanda. In bocca al lupo! – LondonRob

2

Il problema è che le istanze get_highest_row() itself uses row dimensions definiscono la riga massima nel foglio. RowDimension non ha informazioni sulle colonne, il che significa che non possiamo usarlo per risolvere il tuo problema e dobbiamo affrontarlo in modo diverso.

Ecco un tipo di specifica opzione-openpyxl "brutto" che anche se non avrebbe funzionato se use_iterators=True:

from openpyxl.utils import coordinate_from_string 

def get_maximum_row(ws, column): 
    return max(coordinate_from_string(cell)[-1] 
       for cell in ws._cells if cell.startswith(column)) 

Usage:

print get_maximum_row(ws, "A") 
print get_maximum_row(ws, "B") 
print get_maximum_row(ws, "C") 
print get_maximum_row(ws, "D") 

A parte questo, vorrei seguire le @ LondonRob di suggerimento di analizzare il contenuto con pandas e lasciare che faccia il lavoro.

+0

Ho fatto un tentativo di aggiornare il 'wb = load_workbook ('book.xlsx')'. Tuttavia ho ottenuto questo ** Traceback (ultima chiamata ultima): File "C: \ xx.py", riga 53, in stampa get_maximum_row (ws, "A") File "C: \ xx.py" , riga 51, in get_maximum_row per cella in ws._cells se cell.startswith (colonna)) File "C: \ xx.py", riga 51, in per cella in ws._cells se cell.startswith (colonna)) NameError: il nome globale "coordinate_da_stringa" non è definito ** – egodial

+0

sicuro, aggiornato. – alecxe

+0

Forse ho copiato qualcosa di sbagliato qui, ma restituisce lo stesso valore per qualsiasi Col. 'da openpyxl load_workbook importazione wb = load_workbook ('libro.xlsx') WS = wb.get_sheet_by_name ('Foglio1') da openpyxl.utils importazione coordinate_from_string def get_maximum_row (WS, colonna): ritorno max (coordinate_from_string (cella) [- 1] per cellulare in ws._cells se cell.startswith (colonna)) stampa get_maximum_row (WS, "a") stampa get_maximum_row (ws, "B") stampa get_maximum_row (ws, "C") stampa get_maximum_row (ws, "F") 'Grazie per il duro lavoro con questo codice! – egodial

0

Se questa è una limitazione di openpyxl allora si potrebbe provare uno dei seguenti approcci:

  • convertire il file di Excel in formato CSV e utilizzare il modulo Python csv.
  • decomprimere il file di Excel utilizzando zipfile e quindi passare alla sottocartella "xl/worksheets" del file non compresso, e lì troverete un XML per ciascuno dei fogli di lavoro. Da lì è possibile analizzare e aggiornare con BeautifulSoup o lxml.

Il formato xslx Excel è una cartella ad albero compressa (zippata) di file XML. È possibile trovare la specifica here.

0

Figura Inizierò a restituire alla comunità StackOverflow. La soluzione di Alecxe non ha funzionato per me e non volevo usare Pandas, quindi l'ho fatto invece. Esso controlla dalla fine del foglio di calcolo e ti dà il successivo riga/colonna vuota nella soluzione D.

def unassigned_row_in_column_D(): 
    ws_max_row = int(ws.max_row) 
    cell_coord = 'D' + str(ws_max_row) 
    while ws.cell(cell_coord).value == None: 
     ws_max_row -= 1 
     cell_coord = 'D' + str(ws_max_row) 
    ws_max_row += 1 
    return 'D' + str(ws_max_row) 

#then add variable data = 'xxx' to that cell 

ws.cell(unassigned_row_in_column_D()).value = data 
0

di alexce non ha funzionato per me. E 'probabilmente una questione di versione openpyxl, io sono su 2.4.1, ecco cosa ha funzionato dopo un piccolo ritocco:

def get_max_row_in_col(ws, column): 
    return max([cell[0] for cell in ws._cells if cell[1] == column]) 
Problemi correlati