2010-10-13 6 views
5

Mam katalogu 9 obrazów:Jak skutecznie wyświetlić listę sekwencji obrazów? Numercial porównanie sekwencji w Pythonie

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

chciałbym móc wymienić je w sposób efektywny, tak (4 wpisy dla 9 obrazów):

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

Czy istnieje sposób w Pythonie, aby zwrócić katalog obrazów, w krótki/wyraźny sposób (bez listy każdego pliku)?

Więc może coś takiego:

lista wszystkich zdjęć, sortowanie numerycznie, utworzyć listę (licząc każdy obraz w sekwencji od początku). Gdy brakuje obrazu (utwórz nową listę), kontynuuj do momentu zakończenia tworzenia oryginalnej listy plików. Teraz powinienem mieć kilka list, które zawierają niezepsute sekwencje.

Próbuję ułatwić odczytanie/opisanie listy numerów. Gdybym miał sekwencję 1000 kolejnych plików Może to być wyraźnie wymienione jako plik [0001-1000] zamiast pliku ['0001', '0002', '0003' itd. ...]

Edit1 (oparty na sugestie): Biorąc pod uwagę spłaszczoną listę, jak wyprowadzilibyście wzorce globu?

Edit2 Próbuję zepsuć problem na mniejsze kawałki. Oto przykład część rozwiązania: prac dane1 powraca dane2 0010 jako 64, danych3 (dane RealWorld) nie działa:

# 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 

powroty:

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

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

Dlaczego 'image_00 [10-11]' a nie 'image_001 [0-1] '? – eumiro

+0

image_00 [10-11] lub image_001 [0-1], tak myślę, że to jeden mniej znaków – user178686

+0

ironię: tak, istnieje sposób. Wątpię (ale mogę się mylić), że istnieje jakaś funkcja biblioteczna, aby to zrobić. Napisz jakiś kod, zapytaj czegoś bardziej konkretnego (np. Jak mogę porównać łańcuchy podobieństwa) po tym, jak już zrobiłeś 'os.listdir (ścieżka)', itd. –

Odpowiedz

6

Oto realizację tego, co chcesz osiągnąć pracy, stosując kod dodany jako punkt wyjścia:

#!/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 

drukuje ['image_000[1-3]', 'image_00[10-11]', 'image_0011-[1-3]', 'image_0100', 'image_9999'], czyli to, co chcesz.

HISTORIA: Początkowo odpowiedziałem na to pytanie w odwrotnej kolejności, jak @Mark Ransom wskazał poniżej. Ze względu na historię, moja oryginalna odpowiedź brzmiała:

Szukasz glob. Spróbuj:

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

Albo, używając przykładu:

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

Myślę, że to rozwiązanie jest odwrotne od tego, o co pyta. Biorąc pod uwagę spłaszczoną listę, w jaki sposób czerpiesz wzorce globu? –

+0

@ Mark, masz rację, źle zrozumiałem pytanie (a jego tytuł powinien brzmieć "Biorąc pod uwagę spłaszczoną listę, jak wyprowadzić wzorce globu?"). Myślę, że trochę się prześpię, zanim spróbuję:] –

+0

@ Frédéric @Mark. Dziękuję za twoją pomoc. Naprawdę cieszę się z tego problemu. Uczę się jak idę. – 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 

Funkcja increment pozostawiamy jako ćwiczenie dla czytelnika.

Edycja: Oto przykład, w jaki sposób będzie użyty z liczbami całkowitymi zamiast ciągów jako danych wejściowych.

def increment(x): return x+1 

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

Dla każdego sąsiedniego zakresu na wejściu pojawia się para wskazująca początek i koniec zakresu.Jeśli element nie jest częścią zakresu, wartości początkowe i końcowe są identyczne.

+0

dzięki, których tak naprawdę nie rozumiem. Załóżmy, że posortowałem pliki na liście: sorted_list = ["image_0001", "image_0002", "image_0003", "image_0010", "image_0011"] ... czy możesz wyjaśnić, co mi pokazałeś. Dla każdej pozycji na posortowanej liście (jeśli ją zwiększysz, sprawdź, czy istnieje na pozostałej liście) ??? – user178686

+1

@user, ten algorytm testuje każdy element, aby sprawdzić, czy powinien on zostać włączony do bieżącej sekwencji, testując, czy jest równy ostatnie + 1. Jeśli tak, to bieżąca sekwencja jest przedłużona; w przeciwnym razie sekwencja jest tworzona jako krotka, a bieżąca sekwencja jest resetowana do nowego elementu. Jeśli możemy zapewnić, że dane wejściowe nie są puste, można to nawet uprościć. –

+0

Dziękuję. Ok, więc rozumiem, że testuje każdy element, aby zobaczyć, czy jest on równy pierwszemu elementowi + 1. Nie rozumiem "w przeciwnym razie sekwencja jest wydawana jako krotka" ... – user178686

3

Ok, więc znalazłem swoje pytanie być fascynująca zagadka. Zostawiłam jak „skompresować” liczbowy waha się od ciebie (oznaczone jako brak), gdyż istnieje różne sposoby osiągnięcia, że ​​w zależności od tego, jak chcesz to sformatowany i czy chcesz minimalnej liczby elementów lub minimalna długość ciągu znaków: .

Ten roztwór używa prostego wyrażenia regularnego (cyfra łańcuchy) sklasyfikować każdy ciąg na dwie grupy: statyczne i zmiennej. Po zaklasyfikowaniu danych używam groupby do gromadzenia danych statycznych w najdłuższe pasujące grupy, aby uzyskać efekt podsumowania. Łączę wskaźniki liczb całkowitych do wyniku (w matchGrouper), dzięki czemu mogę ponownie wybrać różne części ze wszystkich elementów (w rozpakowaniu).

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] 

Wreszcie dodajemy tyle logiki, aby wykonać ładny wydruk danych wyjściowych i uruchomić przykład.

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) 

Mój katalog obrazów został utworzony na podstawie twojego przykładu. Można zastąpić fnList z poniższej listy do testowania:

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'] 

A kiedy biegnę przed tym katalogu, moje wyjście wygląda następująco:

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

Dzięki, jestem bardzo pewien, co robisz. Czy mógłbyś dodać komentarze, które pomogłyby mi odnieść się do przykładu image_0002, image_0003 itd ... Jeśli mógłbyś dodać listę testową, mógłbym krok po kroku i uruchomić twoje rozwiązanie kawałek po kawałku. – user178686

+0

THANKYOU za twój czas Shane. Będę dalej patrzył na twoje rozwiązanie itertools; Myślę, że mogę się z tego wiele nauczyć. Edycja2 w oryginalnym wpisie była rezultatem wyszukiwania go/studiowania dobrze komentowanego rozwiązania. – user178686