2016-03-08 13 views
5

Ho dati 2D a cui voglio applicare più funzioni. Il codice effettivo utilizza xlrd e un file .xlsx, ma fornirò il seguente pannello della caldaia in modo che l'output sia facile da riprodurre.Applicare più funzioni con la mappa

class Data: 
    def __init__(self, value): 
     self.value = value 

class Sheet: 
    def __init__(self, data): 
     self.data = [[Data(value) for value in row.split(',')] for row in data.split('\n')] 
     self.ncols = max(len(row) for row in self.data) 

    def col(self, index): 
     return [row[index] for row in self.data] 

Creazione di un foglio:

fake_data = '''a, b, c, 
       1, 2, 3, 4 
       e, f, g, 
       5, 6, i, 
       , 6, , 
       , , , ''' 

sheet = Sheet(fake_data) 

In questo oggetto, data contiene una matrice 2D di stringhe (per il formato di input) e voglio eseguire operazioni sulle colonne di questo oggetto. Niente fino a questo punto è nel mio controllo.

voglio fare tre cose a questa struttura: trasporre le righe in colonne, estrarre value da ogni oggetto Data, e provare a convertire il valore in una float. Se il valore non è un float, dovrebbe essere convertito in uno str con spazio bianco spogliato.

from operators import attrgetter 

# helper function 
def parse_value(value): 
    try: 
     return float(value) 
    except ValueError: 
     return str(value).strip() 

# transpose 
raw_cols = map(sheet.col, range(sheet.ncols)) 

# extract values 
value_cols = (map(attrgetter('value'), col) for col in raw_cols) 

# convert values 
typed_cols = (map(parse_value, col) for col in value_cols) 

# ['a', 1.0, 'e', 5.0, '', ''] 
# ['b', 2.0, 'f', 6.0, 6.0, ''] 
# ['c', 3.0, 'g', 'i', '', ''] 
# ['', 4.0, '', '', '', ''] 

Si può notare che map viene applicata ad ogni colonna due volte. In altre circostanze, voglio applicare una funzione a ciascuna colonna più di due volte.

C'è un modo migliore per mappare più funzioni alle voci di un iterabile? Più oltre, c'è via per evitare la comprensione del generatore e applicare direttamente la mappatura a ogni singolo iterabile? O c'è un modo migliore ed estensibile per affrontare tutto questo insieme?

Si noti che questa domanda non è specifica per xlrd, è solo il caso di utilizzo corrente.

+0

promemoria: 'map (f, carta (g, xs))' ha lo stesso output 'map (comporre (f, g), xs)'; il primo eseguirà due volte la raccolta, ma quest'ultimo verrà ripetuto solo una volta. – naomik

+0

@naomik Giusto, vorrei solo che ci fosse un built-in 'compose' credo. –

+1

Jared, puoi facilmente crearne uno tuo. 'compose' non è altro che' lambda f, g: lamda x: f (g (x)) '. Oppure usa semplicemente 'map (lambda x: f (g (x)), xs)' – naomik

risposta

2

Sembra che la soluzione più semplice sia quella di eseguire la propria funzione che applicherà più funzioni allo stesso iterabile.

def map_many(iterable, function, *other): 
    if other: 
     return map_many(map(function, iterable), *other) 
    return map(function, iterable) 

Il rovescio della medaglia è che l'utilizzo è invertito da map(function, iterable) e sarebbe imbarazzante per estendere map ad accettare argomenti (come si può in Python 3.x).

utilizzati:

map_many([0, 1, 2, 3, 4], str, lambda s: s + '0', int) 
# [0, 10, 20, 30, 40] 
4

Si può facilmente del club negli ultimi due map chiamate utilizzando un lambda,

typed_cols = (map(lambda element:parse_value(element['value']), col) 
       for col in value_cols) 

Mentre è possibile bastone simile in analisi e l'estrazione all'interno Sheet.col, IMO che interesserebbe la leggibilità del codice.