Python 快速计算增量的方法 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
JCZ2MkKb5S8ZX9pq
V2EX    Python

Python 快速计算增量的方法

  •  
  •   JCZ2MkKb5S8ZX9pq 2019 年 12 月 11 日 5043 次点击
    这是一个创建于 2231 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设表

    data = [ ['20:00', 100], ['20:10', 130], ['20:20', 190], ... ] 

    期望结果,只统计增量:

    data = [ ['20:00', 100], ['20:10', 30], ['20:20', 60], ... ] 

    正常写法我会,求一个最 pythonic 的搞法。
    印象中好像 map 还是 reduce 啥的能搞这个,但是 py3 好像要导入模块,有点想不起来了。
    谁给指点一下。

    32 条回复    2019-12-12 14:59:37 +08:00
    necomancer
        1
    necomancer  
       2019 年 12 月 11 日
    In [1]: [data[0], list(map(lambda x, y: [x[0],y[1]-x[1]], *zip(data[:-1],data[1:])))]
    Out[1]: [['20:00', 100], [['20:00', 30], ['20:10', 60]]]
    watsy0007
        2
    watsy0007  
       2019 年 12 月 11 日
    ```python
    fst = lambda x: x[0]
    sec = lambda x: x[1]
    [[fst(item), sec(item) - (0 if idx == 0 else sec(data[idx-1]))] for idx, item in enumerate(data)]
    ```
    necomancer
        3
    necomancer  
       2019 年 12 月 11 日
    sorry

    [data[0]] + list(map(lambda x, y: [x[0],y[1]-x[1]], *zip(data[:-1],data[1:])))
    necomancer
        4
    necomancer  
       2019 年 12 月 11 日
    再 sorry,没看清题……
    In [3]: [data[0]] + list(map(lambda x, y: [y[0],y[1]-x[1]], *zip(data[:-1],data[1:])))
    Out[3]: [['20:00', 100], ['20:10', 30], ['20:20', 60]]
    JCZ2MkKb5S8ZX9pq
        5
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @necomancer 好像是 zip 这步有点问题,py3.7。
    TypeError: <lambda>() takes 2 positional arguments but 55 were given
    还是 map 的调用方法问题?
    我要试试,这两个方法不大用到。
    JCZ2MkKb5S8ZX9pq
        6
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    顺便给一串测试数据好了,里面是排序后的 tuple。
    [('2019-12-09 19:40:03', 412), ('2019-12-09 20:10:58', 4136), ('2019-12-09 20:41:00', 6634), ('2019-12-09 21:11:22', 9090), ('2019-12-09 21:41:23', 11636), ('2019-12-09 22:11:27', 14597), ('2019-12-09 22:41:32', 17506), ('2019-12-09 23:11:43', 20315), ('2019-12-09 23:41:44', 22926), ('2019-12-10 00:11:46', 24749), ('2019-12-10 00:41:55', 26133), ('2019-12-10 01:11:58', 27021), ('2019-12-10 01:42:06', 27574), ('2019-12-10 03:08:38', 28333), ('2019-12-10 03:38:48', 28435), ('2019-12-10 04:08:58', 28527), ('2019-12-10 04:39:01', 28608), ('2019-12-10 05:09:23', 28680), ('2019-12-10 05:39:32', 28754), ('2019-12-10 06:09:46', 28841), ('2019-12-10 06:39:51', 29006), ('2019-12-10 07:09:57', 29320), ('2019-12-10 07:40:05', 29755), ('2019-12-10 08:10:51', 30309), ('2019-12-10 08:40:59', 30902), ('2019-12-10 09:11:08', 31454), ('2019-12-10 09:41:21', 31977), ('2019-12-10 10:11:48', 32555), ('2019-12-10 10:42:04', 33137), ('2019-12-10 11:12:30', 33749), ('2019-12-10 11:42:45', 34279), ('2019-12-10 12:12:55', 34964), ('2019-12-10 12:43:05', 35904), ('2019-12-10 13:13:27', 36774), ('2019-12-10 13:43:49', 37375), ('2019-12-10 14:13:56', 37803), ('2019-12-10 14:44:08', 38222), ('2019-12-10 15:14:40', 38589), ('2019-12-10 15:44:51', 38937), ('2019-12-10 16:15:04', 39311), ('2019-12-10 16:45:37', 39707), ('2019-12-10 17:15:53', 40081), ('2019-12-10 17:45:55', 40520), ('2019-12-10 18:16:05', 40991), ('2019-12-10 18:46:48', 41481), ('2019-12-10 19:17:01', 41999), ('2019-12-11 03:23:55', 47926), ('2019-12-11 05:24:01', 48053), ('2019-12-11 07:24:05', 48245), ('2019-12-11 09:24:18', 48742), ('2019-12-11 11:24:28', 49315), ('2019-12-11 13:24:41', 50181), ('2019-12-11 15:24:51', 50767), ('2019-12-11 17:24:52', 51305), ('2019-12-11 19:24:54', 52048), ('2019-12-11 21:25:34', 52911)]
    JCZ2MkKb5S8ZX9pq
        7
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @necomancer 搜了下,看到 pandas 有个 diff 方法。
    [pandas.DataFrame.diff pandas 0.25.3 documentation]( https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.diff.html)
    necomancer
        8
    necomancer  
       2019 年 12 月 11 日
    是……我脑残了……
    [data[0]] + list(map(lambda x: [x[1][0],x[1][1]-x[0][1]], zip(data[:-1],data[1:])))
    ClericPy     9
    ClericPy  
       2019 年 12 月 11 日
    ![uTools_1576073918431.png]( https://i.loli.net/2019/12/11/aR6rG5JXbQoUql8.png)

    以前写过类似的有序一维数组判断间隔的, 取前几行改改也能用, 主要就是一行

    diffs = [nums[i + 1] - nums[i] for i in range(len(nums) - 1)]

    pythonic 这种东西不是炫技, 牺牲可读性写出来的代码基本等同于蠢
    necomancer
        10
    necomancer  
       2019 年 12 月 11 日
    @ClericPy nums[i]这样会引入一个 O(N) 操作的。
    ipwx
        11
    ipwx  
       2019 年 12 月 11 日
    你这个例子写个 for 难道不是可读性最好的么?

    在这里追求所谓的“Pythonic”未免走火入魔了。
    necomancer
        12
    necomancer  
       2019 年 12 月 11 日
    @JCZ2MkKb5S8ZX9pq pandas 我用得不多。pandas 倒是支持时间作差,但必须是 pandas 的 datetime 类型,字符串直接带入会报错。
    JCZ2MkKb5S8ZX9pq
        13
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @necomancer
    df = pd.DataFrame(data, columns=['time', 'volume'])
    df['volume'] = df['volume'].diff()
    df['volume'].fillna(0, inplace=True)
    df['volume'] = df['volume'].astype(int)
    print(df)

    diff 可以应用在单独的 series,就是它不打 0 而是用 nan,还要再转一下。
    necomancer
        14
    necomancer  
       2019 年 12 月 11 日   1
    @JCZ2MkKb5S8ZX9pq 这样啊,谢谢。不过我看求 diff 的时候 pd 好像没有 numpy.diff 里的 prepend 功能,所以无论如何少一个。按你的要求是第一个值不变,原始数据应该在头一个加一个['---', 0] 进去吧~
    necomancer
        15
    necomancer  
       2019 年 12 月 11 日
    或者直接 df['volume'] = numpy.diff(df['volume'], prepend=0)
    JCZ2MkKb5S8ZX9q
        16
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @necomancer
    其实感觉这么搞,适用性的确不高。
    如果数据内部想标清楚点,用 dict 来记录每一条,写起来更长,的确嵌套起来不大好读。
    pandas 在这种地方还是挺好的。

    以前都是简单表格直接 prettyTable,感觉 pandas 还是值得深入搞搞。
    JCZ2MkKb5S8ZX9pq
        17
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @necomancer 没错,pandas 第一个搞了 0 出来。你不说我还没注意。
    JCZ2MkKb5S8ZX9pq
        18
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @necomancer
    这样插行再拿掉感觉也挺蠢的,一整套还是自己写个方法封起来用算了。
    numpy 我不大熟,你感觉 numpy 和 pandas 哪个值得优先地深入看看?
    JCZ2MkKb5S8ZX9pq
        19
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @ClericPy 你这个是算间隔的话,长度 1 的时候为什么不是返回 0 ?
    necomancer
        20
    necomancer  
       2019 年 12 月 11 日
    @JCZ2MkKb5S8ZX9pq numpy 是基础。我是做物理的所以很少处理非数字的数据,pandas 除了 read_csv 啥的没咋用过。
    但 pandas 的 dataframe 的底层似乎就是 numpy.ndarray,比如 df.values 会返回 numpy.ndarray ; numpy 的函数也更数学化。但 pandas 的 dataframe 针对各种复杂的数据似更友好一些。
    necomancer
        21
    necomancer  
       2019 年 12 月 11 日
    numpy 去官方文档看看那个入门了解一下各种数组的操作就可以了。你要是处理数据尤其是简单统计一类的比较多的话,多看看 pandas。有些像 dataframe.diff 不那么好用的场景毕竟还可以用 numpy.diff (prepend,append 和 axis ),毕竟数据应该是一样的。
    dongxiao
        22
    dongxiao  
       2019 年 12 月 11 日   1
    1、
    data[0:1] + list(map(lambda obj1, obj2: [obj1[0], obj2[1]-obj1[1]], data[0:-1], data[1:]))

    2、
    from pandas import DataFrame
    frame = DataFrame(data)
    pd.concat([frame.loc[:, 0], frame.loc[:, 1].map(int).diff()], axis=1).combine_first(frame)
    JCZ2MkKb5S8ZX9pq
        23
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @necomancer 好的,我去了解一下。
    JCZ2MkKb5S8ZX9pq
        24
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 11 日
    @dongxiao 这个是会玩 pandas 的,让我好好消化一下。
    dongxiao
        25
    dongxiao  
       2019 年 12 月 11 日   1
    3、
    import numpy as np
    arr = np.array(data)
    time_arr, num_arr = np.hsplit(arr, [1])
    np.c_[time_arr, np.r_[num_arr[:1], np.diff(num_arr.astype(int), 1, 0)]]

    # 如果要格式转换则
    list(map(lambda obj: [obj[0], int(obj[1])], np.c_[time_arr, np.r_[num_arr[:1], np.diff(num_arr.astype(int), 1, 0)]].tolist()))
    ClericPy
        26
    ClericPy  
       2019 年 12 月 12 日
    @JCZ2MkKb5S8ZX9pq #23 呃, 看了这么多回帖才知道你是要数据分析量级比较大的, 那走 pandas 肯定是最好的选择, 性能高一大截还不容易出错

    @necomancer #10 好吧, 我还真没想到复杂度层面, 以前被 CTO 逮住教训了半天别太纠结语法糖, 写过太多屎山, 所以把 pythonic 就直接看作优雅的可读性了
    dangyuluo
        27
    dangyuluo  
       2019 年 12 月 12 日
    最 pythonic 的写法难道不是用 numpy 么哈哈,shift 第二列然后减一下
    cassidyhere
        28
    cassidyhere  
       2019 年 12 月 12 日
    from itertools import chain

    l = [100, 130, 190]
    [i - j for i, j in zip(l, chain([0], l[:-1]))]

    这样吧,如果想用 map 还可以用 operator

    import operator

    map(operator.sub, l, chain([0], l[:-1]))
    cassidyhere
        29
    cassidyhere  
       2019 年 12 月 12 日
    涉及循环或序列里元素间的操作,可以看看 itertools, operator, functools
    wliansheng
        30
    wliansheng  
       2019 年 12 月 12 日
    为什么楼上大部分回答都不能成功运行。除了 @cassidyhere 可以得到 [100, 30, 60]
    JCZ2MkKb5S8ZX9pq
        31
    JCZ2MkKb5S8ZX9pq  
    OP
       2019 年 12 月 12 日
    @cassidyhere
    你这一写,我感觉以前看 cookbook 的时候都见过,太久不用都还回去了。/捂脸

    不过这个题目我后来想想,还是有一些细节可以探讨。几种方法效率上还是有点区别。
    cfwyy
        32
    cfwyy  
       2019 年 12 月 12 日
    不知道啥叫 pythonic,见笑了。

    newdata=data[0:1]
    newdata.extend([[data[i][0],data[i][1]-data[i-1][1]] for i in range(1,len(data))])
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4036 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 04:12 PVG 12:12 LAX 20:12 JFK 23:12
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86