2009-12-31 9 views
5

Ho una griglia (6 righe, 5 colonne):Rimuovere riga o colonna dall'elenco 2D se tutti i valori (in quella riga o colonna) sono Nessuno

grid = [ 
     [None, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, None, None], 
     ] 

I aumentare la griglia e potrebbe trasformarsi in qualcosa di simile:

grid = [ 
     [{"some" : "thing"}, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, {"something" : "else"}, None], 
     [None, {"another" : "thing"}, None, None, None], 
     [None, None, None, None, None], 
     ] 

voglio rimuovere intere righe e colonne che hanno tuttiNone s in loro. Così nel codice precedente, griglia si trasformerebbe in:

grid = [ 
     [{"some" : "thing"}, None, None], 
     [None, None, {"something" : "else"}], 
     [None, {"another" : "thing"}, None], 
     ] 

ho rimosso riga 1, 2, 5 (zero indicizzato) e la colonna 2 e 4.

Il modo che sto eliminando le righe ora:

for row in range(6): 
    if grid[row] == [None, None, None, None, None]: 
     del grid[row] 

non ho un modo dignitoso di eliminare ancora None colonne. C'è un modo "pitonico" per farlo?

risposta

7

Non è il modo più veloce, ma penso che sia abbastanza facile da capire:

def transpose(grid): 
    return zip(*grid) 

def removeBlankRows(grid): 
    return [list(row) for row in grid if any(row)] 

print removeBlankRows(transpose(removeBlankRows(transpose(grid)))) 

uscita:

[[{'some': 'thing'}, None, None], 
[None, None, {'something': 'else'}], 
[None, {'another': 'thing'}, None]] 

Come funziona: io uso zip di scrivere una funzione che traspone le righe e colonne. Una seconda funzione removeBlankRows rimuove le righe in cui tutti gli elementi sono Nessuno (o qualsiasi cosa che restituisce false in un contesto booleano). Quindi per eseguire l'intera operazione traspongo la griglia, rimuovi le righe vuote (che sono le colonne nei dati originali), trasponi di nuovo, quindi rimuovi le righe vuote.

Se è importante mettere a nudo solo nessuno e non altre cose che danno come risultato falso, modificare la funzione removeBlankRows a:

def removeBlankRows(grid): 
    return [list(row) for row in grid if any(x is not None for x in row)] 
+0

Grazie! Funziona perfettamente. – Matt

+0

Si verifica un errore se valori simili a "" "o' [] 'devono essere distinti da Nessuno. È anche solo leggermente più di 2 volte più lento della mia risposta, 26.8: 12.3 qui (quando si usa la stessa forma di any() in entrambi, apparentemente 'any (n non è None per n in x)' è molto più lento di 'any (x) '). –

+0

Sì, sapevo che questo non sarebbe stato il modo più veloce, ma ritengo che una soluzione più lenta sia accettabile se è più facile da capire e non fa parte del ciclo critico. –

0

Se solo tu avessi una funzione di trasposizione, si potrebbe fare: trasposizione (removeRows (traspongono (removeRows (mat))))

In realtà ... utilizzando una maschera di bit è un'idea migliore.

Fammi pensare a tale proposito ...

primo calcolo gridmask:

grid_mask = [ 
10000, 
00000, 
00000, 
00010, 
00000 
] 

Ora rimuovere gli zeri:

grid_mask = [ 
10000, 
00010, 
] 

ora e tutti i valori bit a bit:

grid_mask = 10010 

Ora rimuovi tutto ma 1a e 4a colonna.

1
grid = ... 

# remove empty rows 
grid = [x for x in grid if any(x)] 
# if any value you put in won't evaluate to False 
# e.g. an empty string or empty list wouldn't work here 
# in that case, use: 
grid = [x for x in grid if any(n is not None for n in x)] 

# remove empty columns 
if not grid: 
    raise ValueError("empty grid") 
    # or whatever, as next line assumes grid[0] exists 
empties = range(len(grid[0])) # assume all empty at first 
for r in grid: 
    empties = [c for c in empties if r[c] is None] # strip out non-empty 
if empties: 
    empties.reverse() # apply in reversed order 
    for r in grid: 
    for c in empties: 
     r.pop(c) 
1

Usa zip() per trasporre la matrice frastagliato, eseguire nuovamente la routine di compensazione, quindi zip() di nuovo.

+3

Si prega di aggiungere i dettagli. –

+0

@Ipthnc: "aggiungi dettaglio" == "scrivi codice" che è ciò che un programmatore dovrebbe essere in grado di fare. –

0

Ecco rapido tentativo

Essa opererà per qualsiasi matrice dimensioni e le righe possono essere diverse taglie, e può essere veloce :)

from collections import defaultdict 
grid = [ 
     [{"some" : "thing"}, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, None, None], 
     [None, None, None, {"something" : "else"}, None], 
     [None, {"another" : "thing"}, None, None, None], 
     [None, None, None, None, None], 
     ] 

# go thru the grid remove, rows which have all None 
# doing that count None in each columns, remove such columns later 
newGrid = [] 
colSize = len(grid) 
colCount = defaultdict(int) 
for row in grid: 
    allNone = True 
    for c, cell in enumerate(row): 
     if cell is None: 
      colCount[c] += 1 
     else: 
      allNone = False 

    if not allNone: # only add rows which are not all none 
     newGrid.append(row) 

# get cols which need to be removed 
removeCols = [col for col, count in colCount.iteritems() if count == colSize] 
removeCols.sort(reverse=True) 

# now go thru each column and remove all None Columns 
for row in newGrid: 
    for col in removeCols: 
     row.pop(col) 

grid = newGrid 
import pprint 
pprint.pprint(grid) 

uscita:

[[{'some': 'thing'}, None, None], 
[None, None, {'something': 'else'}], 
[None, {'another': 'thing'}, None]] 
0

È anche possibile utilizzare la funzione built-in any(), che verifica se uno qualsiasi degli elementi di un iterabile ha un valore non None. È più veloce del confronto e non è necessario conoscere la dimensione del iterabile.

>>> def remove_rows(matrix): 
... '''Returns a matrix without empty rows''' 
... ret_matrix = [] 
... for row in matrix: 
...  #Check if the row has any value or all are None 
...  if any(row): 
...   ret_matrix.append(row) 
... 
... return ret_matrix 
#You can do it also with a list comprehension, which will be even faster 
>>> def remove_rows(matrix): 
...  '''Returns a matrix without empty rows''' 
...  ret_matrix = [ row for row in matrix if any(row) ] 
...  return ret_matrix 

>>> grid = [ 
...   [{"some" : "thing"}, None, None, None, None], 
...   [None, None, None, None, None], 
...   [None, None, None, None, None], 
...   [None, None, None, {"something" : "else"}, None], 
...   [None, {"another" : "thing"}, None, None, None], 
...   [None, None, None, None, None], 
...   ] 


>>> grid = remove_rows(grid) 
>>> grid 
[ 
[{'some': 'thing'}, None, None, None, None], 
[None, None, None, {'something': 'else'}, None], 
[None, {'another': 'thing'}, None, None, None] 
] 

#transpose grid using zip (using asterisk) 
>>> grid = zip(*grid) 
#Note that zip returns tuples 
>>> grid 
[ 
    ({'some': 'thing'}, None, None), 
    (None, None, {'another': 'thing'}), 
    (None, None, None), 
    (None, {'something': 'else'}, None), 
    (None, None, None) 
] 

>>> grid = remove_rows(grid) 
>>> grid 
[ 
    ({'some': 'thing'}, None, None), 
    (None, None, {'another': 'thing'}), 
    (None, {'something': 'else'}, None) 
] 
>>> #Transpose again to get the first matrix, without empty rows or columns 
>>> final_grid = zip(*grid) 
>>> final_grid 
[ 
({'some': 'thing'}, None, None), 
(None, None, {'something': 'else'}), 
(None, {'another': 'thing'}, None) 
] 
Problemi correlati