2010-10-13 7 views
5

Ho un elenco di 9 immagini:Come elencare una sequenza di immagini in modo efficiente? confronto sequenza numerica che in Python

 
image_0001, image_0002, image_0003 
image_0010, image_0011 
image_0011-1, image_0011-2, image_0011-3 
image_9999 

Vorrei essere in grado di elencarli in modo efficiente, come questo (4 voci per 9 immagini):

 
(image_000[1-3], image_00[10-11], image_0011-[1-3], image_9999) 

c'è un modo in python, per restituire un elenco di immagini, in modo breve/chiaro (senza elencare tutti i file)?

Quindi, forse qualcosa di simile:

lista di tutte le immagini, in ordine numerico, creare un elenco (contando ogni immagine in sequenza dall'inizio). Quando manca un'immagine (creare un nuovo elenco), continuare fino al completamento dell'elenco file originale. Ora dovrei avere solo alcune liste che contengono sequenze non spezzate.

Sto cercando di rendere più facile la lettura/descrivere un elenco di numeri. Se ho avuto una sequenza di 1000 file consecutivi Potrebbe essere chiaramente indicato come file [0.001-1.000] piuttosto che file [ '0001', '0002', '0003', ecc ...]

Edit1 (basata su suggerimento): Dato un elenco appiattito, come si deriveranno i pattern glob?

Edit2 Sto cercando di suddividere il problema in parti più piccole. Ecco un esempio di una parte della soluzione: opere data1, i rendimenti Dati2 0010 come 64, dati3 (i dati realworld) non funziona:

# Find runs of consecutive numbers using groupby. The key to the solution 
# is differencing with a range so that consecutive numbers all appear in 
# same group. 
from operator import itemgetter 
from itertools import * 

data1=[01,02,03,10,11,100,9999] 
data2=[0001,0002,0003,0010,0011,0100,9999] 
data3=['image_0001','image_0002','image_0003','image_0010','image_0011','image_0011-2','image_0011-3','image_0100','image_9999'] 

list1 = [] 
for k, g in groupby(enumerate(data1), lambda (i,x):i-x): 
    list1.append(map(itemgetter(1), g)) 
print 'data1' 
print list1 

list2 = [] 
for k, g in groupby(enumerate(data2), lambda (i,x):i-x): 
    list2.append(map(itemgetter(1), g)) 
print '\ndata2' 
print list2 

rendimenti:

data1 
[[1, 2, 3], [10, 11], [100], [9999]] 

data2 
[[1, 2, 3], [8, 9], [64], [9999]] 
+0

Perché 'image_00 [10-11]' 'e non image_001 [0-1] '? – eumiro

+0

image_00 [10-11] o image_001 [0-1], sì, suppongo che sia un carattere in meno – user178686

+0

Cinicamente: sì, c'è un modo. Dubito (ma potrei sbagliarmi) che ci sia qualche funzione di libreria per farlo. Scrivi del codice, chiedi qualcosa di più specifico (ad esempio, come posso confrontare le stringhe per la somiglianza) dopo che hai già fatto 'os.listdir (percorso)', ecc. –

risposta

6

Ecco un realizzazione di ciò che si vuole lavorare per realizzare, utilizzando il codice è stato aggiunto come punto di partenza:

#!/usr/bin/env python 

import itertools 
import re 

# This algorithm only works if DATA is sorted. 
DATA = ["image_0001", "image_0002", "image_0003", 
     "image_0010", "image_0011", 
     "image_0011-1", "image_0011-2", "image_0011-3", 
     "image_0100", "image_9999"] 

def extract_number(name): 
    # Match the last number in the name and return it as a string, 
    # including leading zeroes (that's important for formatting below). 
    return re.findall(r"\d+$", name)[0] 

def collapse_group(group): 
    if len(group) == 1: 
     return group[0][1] # Unique names collapse to themselves. 
    first = extract_number(group[0][1]) # Fetch range 
    last = extract_number(group[-1][1]) # of this group. 
    # Cheap way to compute the string length of the upper bound, 
    # discarding leading zeroes. 
    length = len(str(int(last))) 
    # Now we have the length of the variable part of the names, 
    # the rest is only formatting. 
    return "%s[%s-%s]" % (group[0][1][:-length], 
     first[-length:], last[-length:]) 

groups = [collapse_group(tuple(group)) \ 
    for key, group in itertools.groupby(enumerate(DATA), 
     lambda(index, name): index - int(extract_number(name)))] 

print groups 

Questo stampa ['image_000[1-3]', 'image_00[10-11]', 'image_0011-[1-3]', 'image_0100', 'image_9999'], che è ciò che si desidera.

STORIA: Inizialmente ho risposto alla domanda all'indietro, come sottolineato da @ Mark Ransom. Per il bene della storia, la mia risposta originale era:

Siete alla ricerca di glob. Prova:

import glob 
images = glob.glob("image_[0-9]*") 

Oppure, utilizzando il tuo esempio:

images = [glob.glob(pattern) for pattern in ("image_000[1-3]*", 
    "image_00[10-11]*", "image_0011-[1-3]*", "image_9999*")] 
images = [image for seq in images for image in seq] # flatten the list 
+0

Penso che questa soluzione sia al contrario di quello che la domanda sta ponendo. Dato un elenco appiattito, come si deriveranno i modelli glob? –

+0

@ Mark, hai ragione, ho frainteso la domanda (e il suo titolo dovrebbe essere davvero "Dato un elenco appiattito, come potresti derivare i pattern glob?"). Penso che dormirò prima di dargli un altro tentativo:] –

+0

@ Frédéric @Mark. Grazie per l'aiuto. Mi sto davvero godendo questo problema. Sto imparando mentre vado. – user178686

2
def ranges(sorted_list): 
    first = None 
    for x in sorted_list: 
     if first is None: 
      first = last = x 
     elif x == increment(last): 
      last = x 
     else: 
      yield first, last 
      first = last = x 
    if first is not None: 
     yield first, last 

La funzione increment viene lasciato come esercizio per il lettore.

Modifica: Ecco un esempio di come verrebbe utilizzato con numeri interi anziché stringhe come input.

def increment(x): return x+1 

list(ranges([1,2,3,4,6,7,8,10])) 
[(1, 4), (6, 8), (10, 10)] 

Per ogni intervallo contiguo nell'input si ottiene una coppia che indica l'inizio e la fine del campo.Se un elemento non fa parte di un intervallo, i valori iniziale e finale sono identici.

+0

grazie, non capisco davvero. Quindi, supponiamo di aver ordinato i file in una lista: lista_ordinata = ['image_0001', 'image_0002', 'image_0003', 'image_0010', 'image_0011'] ... puoi spiegare cosa mi hai mostrato. Per ogni elemento nella lista_ordinata (se lo si incrementa, verificare se esiste nel resto dell'elenco) ??? – user178686

+1

@user, questo algoritmo testa ogni elemento per vedere se dovrebbe essere incluso nella sequenza corrente testando per vedere se è uguale all'ultimo + 1. Se lo è, la sequenza corrente viene estesa; altrimenti la sequenza viene restituita come una tupla e la sequenza corrente viene reimpostata sul nuovo elemento. Se possiamo assicurare che l'input non è vuoto, questo potrebbe anche essere semplificato. –

+0

Grazie. Ok, quindi capisco che mette alla prova ogni elemento per vedere se è uguale all'elemento precedente + 1. Non capisco "altrimenti la sequenza è resa come una tupla" ... – user178686

3

Ok, ho trovato la tua domanda un puzzle affascinante. Ho lasciato come " " comprimere "gli intervalli numerici fino a te (contrassegnati come TODO), poiché ci sono diversi modi per farlo a seconda di come ti piace formattato e se vuoi il numero minimo di elementi o la descrizione della stringa minima lunghezza.

Questa soluzione utilizza un'espressione regolare semplice (stringhe di cifre) per classificare ciascuna stringa in due gruppi: statici e variabili. Dopo aver classificato i dati, utilizzo groupby per raccogliere i dati statici nei gruppi di corrispondenza più lunghi per ottenere l'effetto di riepilogo. Mescolo i sentitori dei numeri interi nel risultato (in matchGrouper) in modo da poter riselezionare le parti variabili da tutti gli elementi (in unpack).

import re 
import glob 
from itertools import groupby 
from operator import itemgetter 

def classifyGroups(iterable, reObj=re.compile('\d+')): 
    """Yields successive match lists, where each item in the list is either 
    static text content, or a list of matching values. 

    * `iterable` is a list of strings, such as glob('images/*') 
    * `reObj` is a compiled regular expression that describes the 
      variable section of the iterable you want to match and classify 
    """ 
    def classify(text, pos=0): 
     """Use a regular expression object to split the text into match and non-match sections""" 
     r = [] 
     for m in reObj.finditer(text, pos): 
      m0 = m.start() 
      r.append((False, text[pos:m0])) 
      pos = m.end() 
      r.append((True, text[m0:pos])) 
     r.append((False, text[pos:])) 
     return r 

    def matchGrouper(each): 
     """Returns index of matches or origional text for non-matches""" 
     return [(i if t else v) for i,(t,v) in enumerate(each)] 

    def unpack(k,matches): 
     """If the key is an integer, unpack the value array from matches""" 
     if isinstance(k, int): 
      k = [m[k][1] for m in matches] 
     return k 

    # classify each item into matches 
    matchLists = (classify(t) for t in iterable) 

    # group the matches by their static content 
    for key, matches in groupby(matchLists, matchGrouper): 
     matches = list(matches) 
     # Yield a list of content matches. Each entry is either text 
     # from static content, or a list of matches 
     yield [unpack(k, matches) for k in key] 

Infine, aggiungiamo una logica sufficiente per eseguire una buona stampa dell'output ed eseguire un esempio.

def makeResultPretty(res): 
    """Formats data somewhat like the question""" 
    r = [] 
    for e in res: 
     if isinstance(e, list): 
      # TODO: collapse and simplify ranges as desired here 
      if len(set(e))<=1: 
       # it's a list of the same element 
       e = e[0] 
      else: 
       # prettify the list 
       e = '['+' '.join(e)+']' 
     r.append(e) 
    return ''.join(r) 

fnList = sorted(glob.glob('images/*')) 
re_digits = re.compile(r'\d+') 
for res in classifyGroups(fnList, re_digits): 
    print makeResultPretty(res) 

La mia directory di immagini è stata creata dal tuo esempio. È possibile sostituire fnList con la seguente lista per la prova:

fnList = [ 
'images/image_0001.jpg', 
'images/image_0002.jpg', 
'images/image_0003.jpg', 
'images/image_0010.jpg', 
'images/image_0011-1.jpg', 
'images/image_0011-2.jpg', 
'images/image_0011-3.jpg', 
'images/image_0011.jpg', 
'images/image_9999.jpg'] 

E quando corro contro questa directory, la mia uscita si presenta come:

StackOverflow/3926936% python classify.py 
images/image_[0001 0002 0003 0010].jpg 
images/image_0011-[1 2 3].jpg 
images/image_[0011 9999].jpg 
+0

Grazie, sono molto sicuro di quello che stai facendo. Potresti aggiungere alcuni commenti che mi aiutano a fare riferimento all'esempio image_0002, image_0003, ecc ... Se si potesse aggiungere un elenco di test, potrei essere in grado di scorrere ed eseguire la soluzione un po 'alla volta. – user178686

+0

GRAZIE per il tuo tempo Shane. Continuerò a guardare la tua soluzione itertools; Penso di poter imparare molto da questo. Il Edit2 nel post originale, era il risultato di googling/studiando la tua soluzione ben commentata. – user178686