How to apply function to consecutive pairs of values from a list?

时间:2018-12-03 13:18:04

标签: python python-3.x

I have a dictionary

tickers = {'BTC': [200, 149, 98, 44], 'ETH': [200, 320, 405, 460]}

now and prev are elements of lists.

We compare this element and previous element in list.

For example in BTC:

149 with 200
98 with 149
44 with 98

# check state . describe state
def check_state(now, prev):
    state = None
    if now >= prev:
        if now <= 1.5 * prev:
                state = 0
    if now >= 1.5 * prev:
        state = 1
    if now < prev:
        if now * 1.5 >= prev:
               state = 2
    if now * 1.5 < prev:
        state = 3
    return state

I want to get new dictionary with tickers and states in every day. First day no state because first day didn't have previous.

tickers_state = {'BTC': [None, 3, 3, 3], 'ETH': [None, 1, 0, 0]}

where elements are state in every day of each ticker.

How can i do it?

2 个答案:

答案 0 :(得分:1)

如果将check_state的输入参数的顺序从def check_state(now, prev):转换为def check_state(prev, now):,则将函数应用于列表的连续值对的问题变得非常容易。我想出了以下功能:

import itertools


def apply_pairwise(values,
                   function):
    """
    Applies function to consecutive pairs of elements from an input list
    """
    def pairwise(iterable):
        """
        s -> (s0,s1), (s1,s2), (s2,s3), ...
        """
        a, b = itertools.tee(iterable)
        next(b, None)
        return zip(a, b)

    yield from itertools.chain([None],
                               itertools.starmap(function, pairwise(values)))

用法示例:

>>> btc = [200, 149, 98, 44]
>>> list(apply_pairwise(btc, check_state))
[None, 2, 3, 3]
>>> eth = [200, 320, 405, 460]
>>> list(apply_pairwise(eth, check_state))
[None, 1, 0, 0]

如果您不能撤消输入:

如果不可能更改输入的顺序,我们可以稍微采用一下我们的功能:

import itertools


def apply_pairwise(values,
                   function,
                   *,
                   reverse=False):
    """
    Applies function to consecutive pairs of elements from an input list
    """
    def pairwise(iterable):
        """
        s -> (s0,s1), (s1,s2), (s2,s3), ...
        or -> (s1,s0), (s2,s1), (s3,s2), ... if reverse
        """
        a, b = itertools.tee(iterable)
        next(b, None)

        if reverse:
            return zip(b, a)
        return zip(a, b)

    yield from itertools.chain([None],
                               itertools.starmap(function, pairwise(values)))

,您可以这样使用它:

>>> btc = [200, 149, 98, 44]
>>> list(apply_pairwise(btc, check_state, reverse=True))
[None, 2, 3, 3]
>>> eth = [200, 320, 405, 460]
>>> list(apply_pairwise(eth, check_state, reverse=True))
[None, 1, 0, 0]

说明:

为了获得连续的元素对,我们可以使用itertools配方中的辅助函数:

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

它是这样的:

>>> list(pairwise(range(5)))
[(0, 1), (1, 2), (2, 3), (3, 4)]

现在,对于每对元素,我们想应用您的check_state函数。这里的itertools.starmap可能有用。它是这样的:

>>> list(itertools.starmap(pow, [(2, 3), (2, 10), (10, 3), (3, 4)]))
[8, 1024, 1000, 81]

剩下的唯一事情就是在starmap之前加上None产生的值。当starmap成为iterator时,我们可以使用itertools.chain将第一个None与其余元素组合起来。


P.S .:将其应用于tickers字典的值应该很容易。我会把它留给你。

答案 1 :(得分:1)

您可以将列表中的每对连续值与一个循环遍历该列表并使用枚举功能的功能进行比较,以进行比较。然后将dict comprehension与函数配合使用,以生成具有比较值的原始dict的映射版本。

在下面的示例中,它从第二个列表项开始循环遍历列表的一个切片,因此i的当前值访问前一个元素,因为该切片实际上将索引值移位了1。

tickers = {'BTC': [200, 149, 98, 44], 'ETH': [200, 320, 405, 460]}

def check_states(data):
    states = [None]
    for i, n in enumerate(data[1:]):
        state = None
        p = data[i]
        (low, high) = (p / 1.5, p * 1.5)
        if n >= p:
            state = 0 if n <= high else 1
        else:
            state = 2 if n >= low else 3           
        states.append(state)    
    return states

tickers_state = {k: check_states(v) for k, v in tickers.items()}

print(tickers_state)
# OUTPUT
# {'BTC': [None, 2, 3, 3], 'ETH': [None, 1, 0, 0]}