2012-03-07 43 views
3

我是新来的Python和我坚持试图找出“间隔”,其中y值= < 70.我有一个有序字典词条:确定的间隔一定值

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

我想写一个函数,允许我基于d的键(x值)根据y值= < N来识别“间隔”(a,b)。我的终点(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 – 2012-03-08 00:31:17

+0

谢谢!这是一个错字。 – 2012-03-08 00:36:01

+0

好的,相应地更新了我的答案 – 2012-03-08 00:38:10

回答

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 

编辑:

扩展代码片断接受第一或最后一个项目有y值< = 70和治疗ÿ - 值作为花车

+0

这看起来像我开始写这个循环,所以它接近我的逻辑。感谢你让这漫长的一天变得更短。 – 2012-03-08 00:38:57

+0

如果我提供这个新的字典d,比如说d = {0:'78',35:'61 .33330154',172:'65',110:'59 .66669846'},那么输出是{(141,0): [74.0,69.0,78.0]}。不知道这是为什么发生,但(141,0)是奇怪的。也许这是元终端的问题。例如,我有x值从0到180,所以当在边缘有一个低点时会发生什么,就像我在172(值为65)时所做的那样? – 2012-03-08 00:50:00

+1

好的,我会看看 – 2012-03-08 00:57:39

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

This我知道有一个简单的方法,我没有看到在计算机上花费太多时间后 一个问题:有没有办法处理d作为字典,如我的例子,或者我应该工作转换它到元组??我现在有一个字典中的数据。 – 2012-03-08 00:20:55

+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 2012-03-08 00:29:53

+0

@LillianMilagrosCarrasquillo我现在已经用字典工作了。你得到你的问题中描述的输出。 – Manish 2012-03-08 02:59:32

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

旁注:您的字典有字符串值,而不是int值。在你的例子中,你的意思可能是< =而不是<。

因此,为了更清楚地重申您的问题,您可以:

  • 有点的有序列表(x,y)
  • 有一个阈值T
  • 希望找到点p 的所有连续运行我,...,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相当陌生,并没有遇到过这种情况。 – 2012-03-08 01:10:27

+1

@LillianMilagrosCarrasquillo:这不是必须的,因为你可以像遍历你的字典一样:'d.items()',它将一个遍历'(key,value)'的迭代器作为元组返回到你的列表中。所以'dippingIntervals(d.items())'。请注意,返回的顺序是任意的,但在您的情况下,您使用的是有序字典。我个人本来应该首先避免使用字符串,但如果要改变它太迟了,您可以在d.items()中执行'd = {k:float(v)for k,v''。 – ninjagecko 2012-03-08 01:16:32

+0

谢谢。我正在尝试其他一些可能的东西,并得到一些奇怪的东西。对发生什么事情有任何想法? *********************** >>> c = {k:float(v)for k,v in d.items()} >>> c {0:78.0,35:61.33330154,172:65.0,110:59.66669846} >>> a = list(dippingIntervals(sorted(c.items())))>>> a [] – 2012-03-08 01:23:29

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的解决方案,以避免超前/回顾后,迫使它来存储列表问题:

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

感谢您发表本文。我将在今天晚些时候检查它是如何工作的。我也有点着迷这个问题!这似乎是一个相当普遍的问题,因为我所做的只是确定时间间隔,并且很惊讶没有找到涵盖这个模块的模块。 – 2012-03-08 17:18:42

+0

好吧,这对我来说很有意义,我给它的所有例子似乎都在起作用。如果最后一个值<= 70,那么我无法在一个特定情况下调整它,间隔应该扩展到x值的末尾(即如果n = 180且d [id] [last] <70并且持续<180,间隔输出应该是(a,180)不是(a,last)。对于一个真实的例子:d ['150','172'] = {73,55,55},那么它确实应该是(150,180)而不是(其中180是最大的x值) – 2012-03-09 01:14:06

+0

@LillianMilagrosCarrasquillo,我不完全按照。上面的'a'是什么?给我一个输入字典/序列导致问题,并且你期望的输出,另外,我在第三个版本中注意到了一个错误;我忘记丢弃'_nil'哨兵。 – senderle 2012-03-09 02:43:38