2012-03-07 4 views
3

Я новичок в Python и я застрял, пытаясь определить «интервалы», где у-значение = < 70. У меня есть упорядоченная запись словаря:Определить интервалы с определенными значениями

d = {0: '92', 11: '70', 43: '77', 44: '76', 61: '77', 64: '69', 
        68: '67', 84: '68', 93: '87', 108: '81', 141: '74'} 

Я хочу написать функция, которая позволяет мне идентифицировать «интервалы» (a, b) на основе ключей (x-значений) d на основе y-значений = < N. Мои конечные точки (a, b) должны быть там, где значения начинают спускаться и возрастать в и из этого значения N, поэтому фактические значения конечной точки будут «выше» N, но записи между ними должны быть ниже.

(a,b): {above, below, below, below, above} 

К примеру, я заинтересован в промежутках как словарь, здесь N = 70:

{(0,43):{92,70,77}, (61,93): {77, 69, 67, 68, 87}} <-- includes the values at endpoints 

Но, может игнорировать эти другие «интервалы», где значения никогда не ниже 70 поэтому в этом случае нам не нужны: (43,51), (93,180)

Есть ли простой способ сделать это? До сих пор я мог идентифицировать точки, где происходит переход от «выше» к «ниже» 70 или наоборот, но не уверен, как действовать при создании интервалов и значений (например, в словаре). Кажется, я слишком долго смотрел на это.

+0

В примере вывода используется y-значение <= 70, а не y-значение <70 –

+0

Спасибо! Это была опечатка. –

+0

ОК, обновил свой ответ соответственно –

ответ

1

Следующий код должен дать вам выход вы просили:

oninterval = False 
dd = {} 
keys = d.keys() 
keys.sort() 
start_key, first_val = keys[0], d[keys[0]] 

for k in keys: 
    v = float(d[k]) 
    if oninterval: 
     cur_list.append(v) 
     if not int(v) <= 70: # interval ends 
      oninterval = False 
      dd[(start_key,k)] = cur_list 
    else: 
     if int(v) <= 70: 
      cur_list = [first_val, v] 
      oninterval = True 
     else: 
      start_key, first_val = k, v 

if oninterval: dd[(start_key, keys[-1])] = cur_list 

Edit:

Расширенная код фракции принять первый или последний элемент, чтобы иметь у-значение < = 70 и для лечения у -значения как плавающие

+0

Это похоже на то, как я начал писать этот цикл, так что он близок к моей логике. Спасибо, что сделали этот длинный день немного короче. –

+0

Если я использую этот новый словарь d, скажем d = {0: '78', 35: '61 .33330154 ', 172:' 65 ', 110: '59 .66669846'}, то вывод будет {(141, 0): [74,0, 69,0, 78,0]}. Не уверен, почему это происходит, но что (141,0) - это что-то странное. Возможно, это проблема с мета-оконечными точками. Например, у меня есть значения х от 0 до 180, так что происходит, когда есть низкая точка прямо на краю, как я здесь, на 172 (значение равно 65)? –

+1

ОК, я посмотрю –

1
d = {0: '92', 11: '70', 43: '77', 44: '76', 61: '77', 64: '69', 
     68: '67', 84: '68', 93: '87', 108: '81', 141: '74'} 

r = [] 
k = None 
v = None 

for i in sorted(d.keys()): 
    if not k is None: 
     v.append(d[i]) 

    if int(d[i]) > 70: 
     if k is None: 
      k = [i] 
      v = [d[i]]; 
     else: 
      k.append(i) 
      r.append((tuple(k), v)) 
      k = None 
      v = None 

print r 
+0

Я знаю, что был простой способ, который я не видел, потратив слишком много времени на компьютер. Один вопрос: есть ли способ обработать d в качестве словаря, как в моем примере, или мне нужно преобразовать он имеет кортежи? У меня есть данные в словаре сейчас. –

+2

Когда я запускаю это, он печатает '[[(0, 92), (11, 70), (43, 77)], [(44, 76), (61, 77)], [(93, 87), (108, 81)]], но не должны быть результатом [[(0, 92), (11, 70), (43, 77)] , [(61, 77), (64, 69), (68, 67), (84, 68), (93, 87)]] '? – srgerg

+0

@LillianMilagrosCarrasquillo Я сейчас начал работать со словарем. вы получите результат, как описано в вашем вопросе. – Manish

1

Вот несколько многословным решение:

import collections 

values = [(0, '92'), (11, '70'), (43, '77'), (44, '76'), (61, '77'), (64, '69'), 
    (68, '67'), (84, '68'), (93, '87'), (108, '81'), (141, '74')] 
d = collections.OrderedDict(values) 

def intervals(d, n): 
    result = collections.OrderedDict() 
    interval = list() 
    lastk, lastv, startk = None, None, None 
    for k, v in d.iteritems(): 
     if int(v) > n: 
      if startk is not None: 
       interval.append(int(d[k])) 
       result[(startk, k)] = interval 
       interval = list() 
       startk = None 
     else: 
      if lastv: 
       interval.append(int(d[lastk])) 
       startk = lastk 
      interval.append(int(d[k])) 
     lastk, lastv = k, int(v) > n 
    return result 


if __name__ == '__main__': 
    print intervals(d, 70) 

При запуске этого он печатает:

OrderedDict([((0, 43), [92, 70, 77]), ((61, 93), [77, 69, 67, 68, 87])]) 

, который является желаемым результатом.

1

sidenote: Ваш словарь имеет строковые значения, а не значения int. И вы можете указать < = вместо <, в вашем примере.

Таким образом, чтобы пересчитать вашу проблему более четко, вам:

  • Есть упорядоченный список точек (x,y)
  • имеют порог значения Т
  • желание найти все последовательные прогоны точек р я, ..., p j такой, что конечные точки> T, но другие точки не являются; т. е. все участки, где точки «опускаются и выходят» из пороговой линии. (Обратите внимание, что такие прогоны могут перекрываться, например.[71,70,{71],70,71})

Алгоритм будет выглядеть следующим образом:

from itertools import * 

def dippingIntervals(points, threshold=70): 
    yBelowThreshold = lambda i: points[i][1]<=threshold 

    for below,g in groupby(range(len(points)), yBelowThreshold): 
     if below: 
      interval = list(g) 
      start,end = interval[0],interval[-1] 
      if start>0 and end<len(points)-2:  #modify if "open" intervals also desired 
       yield points[start-1 : end+2] 

Демо:

>>> d = [(0, 92), (11, 70), (43, 77), (44, 76), (61, 77), (64, 69), (68, 67), (84, 68), (93, 87), (108, 81), (141, 74)] 
>>> pprint(list(dippingIntervals(d))) 
[((0, 92), (11, 70), (43, 77)), 
((61, 77), (64, 69), (68, 67), (84, 68), (93, 87))] 

Вы можете постобработку данных без особых хлопот, например, чтобы получить его в формат, который вы хотели, изменить вышеуказанную функцию следующим образом:

... yield (start,end), {xy[1] for xy in points[start-1 : end+2]} 

Недостатком этого метода является то, что он не работает на итераторах; следующий будет работать на итераторы, и более «классический» способ сделать это:

def getY(point): 
    return point[1] 

def dippingIntervals(points, threshold=70, key=getY): 
    """ 
     Returns runs of points whose y-values dip below intervals 
     >>> list(dippingIntervals([71,70,74,64,64,70,71], key=lambda x:x)) 
     [(71, [70], 74), 
     (74, [64, 64, 70], 71)] 
    """ 
    def match(point): 
     return key(point)<=threshold 

    lastP = None 
    for p in points: 
     if lastP==None: 
      lastP = p 
      continue 

     if not match(lastP) and match(p): 
      start = lastP 
      R = [p] 
     elif match(lastP) and match(p): 
      R += [p] 
     elif match(lastP) and not match(p): 
      end = p 
      yield start,R,end 

     lastP = p 
+0

Спасибо! Итак, я хочу создать словарь с кортежами вместо того, как он есть сейчас? То есть d = {0: '78', 35: '61 .33330154 ', 172:' 65 ', 110: '59 .66669846'} должно быть d = [(0,78), (35,61.33330154), (172, 65), (110, 59,66669846)]? Я довольно новичок в Python и не сталкивался с этим. –

+1

@LillianMilagrosCarrasquillo: это необязательно, так как вы можете просто перебирать свой словарь так: 'd.items()', который возвращает итератор поверх пар '(key, value)' в вашем списке в виде кортежей. Итак 'dippingIntervals (d.items())'. Обратите внимание, что возвращенный заказ произволен, но в вашем случае вы используете упорядоченный словарь. Лично я бы избежал использования строк в первую очередь, но если это слишком поздно, вы можете сделать это: d = {k: float (v) для k, v в d.items()} '. – ninjagecko

+0

спасибо. Я пытаюсь сделать что-то другое, и я получаю что-то странное. Любые мысли о том, что происходит? *********************** >>> c = {k: float (v) для k, v в d.items()} >>> c {0: 78.0, 35: 61.33330154, 172: 65.0, 110: 59.66669846} >>> a = list (dippingIntervals (отсортировано (c.items()))) >>> a [] –

3

По причинам, я не могу полностью объяснить, эта проблема загипнотизированы меня. Но я думаю, что, наконец, я получил его из своей системы. Во-первых, основной, чистый и простое решение:

intervals = [[]] 
prev = None 
sorted_items = sorted(d.iteritems()) 
for k, v in sorted_items: 
    if v <= 70: 
     ext = (k,) if (intervals[-1] or prev is None) else (prev, k) 
     intervals[-1].extend(ext) 
    elif intervals[-1]: 
     intervals[-1].append(k) 
     intervals.append([]) 
    prev = k 

if not intervals[-1]: 
    intervals.pop() 

print dict(((iv[0], iv[-1]), [d[k] for k in iv]) for iv in intervals) 

Это достаточно легко абстрагироваться выше, чтобы создать итератор:

def iter_intervals(vals, filter_f, _nil=object()): 
    prev = _nil 
    interval = [] 
    for x in vals: 
     if filter_f(x): 
      ext = (x,) if (interval or prev is _nil) else (prev, x) 
      interval.extend(ext) 
     elif interval: 
      interval.append(x) 
      yield interval 
      interval = [] 
     prev = x 
    if interval: 
     yield interval 

intervals = iter_intervals(d.iteritems(), lambda x: x[1] <= 70) 
print dict(((iv[0][0], iv[-1][0]), [v for k, v in iv]) for iv in intervals) 

Но что должен хранить много состояния. Интересно, есть ли способ сделать меньше, что ...

def iter_intervals(vals, filter_f, _nil=object()): 
    iters = itertools.tee(itertools.chain((_nil,), vals, (_nil,)), 3) 
    next(iters[1]); next(iters[2]); next(iters[2]) 
    triplets = itertools.izip(*iters) 
    interval = set() 
    for p, curr, n in triplets: 
     if filter_f(curr): 
      interval.update((p, curr, n)) 
     elif interval: 
      interval.discard(_nil) 
      yield sorted(interval) 
      interval = set() 
    if interval: 
     interval.discard(_nil) 
     yield sorted(interval) 

intervals = iter_intervals(d.iteritems(), lambda x: x[1] <= 70) 
print dict(((iv[0][0], iv[-1][0]), [v for k, v in iv]) for iv in intervals) 

Сделав это, теперь это более очевидным, как адаптировать ninjagecko «s решение, чтобы избежать предпросмотр/просмотра назад проблема, которая заставила его хранить список :

def framed_intervals(points, filter_f, _nil=object()): 
    iters = itertools.tee(itertools.chain((_nil,), points, (_nil,)), 3) 
    next(iters[1]); next(iters[2]); next(iters[2]) 
    triplets = itertools.izip(*iters) 
    for below, group in itertools.groupby(triplets, lambda x: filter_f(x[1])): 
     if below: 
      interval = set(itertools.chain.from_iterable(group)) 
      interval.discard(_nil) # or continue if None in interval to 
      yield sorted(interval) # drop incomplete intervals 

intervals = framed_intervals(d.iteritems(), lambda x: x[1] <= 70) 
print dict(((iv[0][0], iv[-1][0]), [v for k, v in iv]) for iv in intervals) 
+0

Благодарим вас за сообщение. Я буду проверять, как это работает позже сегодня. Я тоже немного одержим этой проблемой! Это похоже на довольно распространенную проблему, поскольку все, что я делаю, это определение интервалов, и я удивлен тем, что не нашел модуль, который охватывает это. –

+0

Хорошо, это имеет смысл для меня, и все примеры, которые я им даю, похоже, работают. Мне трудно адаптировать его в одном конкретном случае, если последнее значение <= 70, интервал должен быть расширен до конца значений x (то есть, если n = 180 и d [id] [last] <70 и последний <180, выходной интервал должен быть (a, 180) не (a, последним). Для реального примера: d ['150', '172'] = {73,55,55}, тогда это действительно должно быть что вместо этого интервал равен (150 180) (где 180 - максимальное значение x). –

+0

@LillianMilagrosCarrasquillo, я не полностью следую. Что такое 'a' выше? Покажите мне входной dict/sequence, который вызывает проблему, и ожидаемый результат. Кроме того, я заметил ошибку в третьей версии, я забыл отказаться от часового '_nil'. – senderle

Смежные вопросы