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
zxCoder
V2EX  ›  Python

这段代码在 Python 里能怎么改写成效率更高的吗,听说 for 循环比较慢

  •  
  •   zxCoder · 2021-10-07 09:08:34 +08:00 · 6155 次点击
    这是一个创建于 904 天前的主题,其中的信息可能已经有所发展或是发生改变。
        xs = []
        ys = []
        zs = []
        for data in ls:
            _x, _y, _z = data
            xs.append(_x)
            ys.append(_y)
            zs.append(_z)
    
    33 条回复    2021-10-10 11:38:52 +08:00
    fuis
        1
    fuis  
       2021-10-07 09:16:43 +08:00
    用 numpy

    xs = ls[:,0]
    ys = ls[:,1]
    zs = ls[:,2]
    fuis
        2
    fuis  
       2021-10-07 09:17:27 +08:00   ❤️ 1
    ls = numpy.array(ls)
    ChrisFreeMan
        3
    ChrisFreeMan  
       2021-10-07 09:24:41 +08:00
    等待 python3.11 版本,听说有一倍速度的提升🐶
    zxCoder
        4
    zxCoder  
    OP
       2021-10-07 09:26:55 +08:00
    @fuis 不是数字呃,numpy 能处理非数字吗
    vcfghtyjc
        5
    vcfghtyjc  
       2021-10-07 09:31:18 +08:00
    不太清楚具体需求,也许可以让 xs,ys,zs 变成迭代器而不是 list ?
    zacharyjia
        6
    zacharyjia  
       2021-10-07 09:31:50 +08:00   ❤️ 1
    不考虑数组转 np array 的这个开销的话,1 楼的 numpy 确实非常快:

    Using numpy: 0.00000000s
    Using append: 0.36167359s


    加上转 np 的开销嘛,就不一样了:

    Using numpy: 1.69339228s
    Using append: 0.37969041s


    其实 append 还是挺快的,比非常 Pythonic 的*zip 的方法要快挺多了:
    Using *zip: 1.30143762s
    Using append: 0.39510083s


    参考:
    https://stackoverflow.com/questions/8081545/how-to-convert-list-of-tuples-to-multiple-lists
    catbaron
        7
    catbaron  
       2021-10-07 10:00:53 +08:00 via iPhone   ❤️ 1
    zip 怎么样
    fatestigma
        8
    fatestigma  
       2021-10-07 10:10:59 +08:00   ❤️ 3
    for 循环不知道怎么去掉,但是有一个提速的方法,list 初始化的时候带上长度
    xs = [None] * len(data)
    对于比较大的 list,可以快那么几十毫秒。。
    ch2
        9
    ch2  
       2021-10-07 10:14:25 +08:00
    用列表推导
    xs=[data[0] for data in ls]
    ys=[data[1] for data in ls]
    zs=[data[2] for data in ls]
    ch2
        10
    ch2  
       2021-10-07 10:18:14 +08:00
    用 map 运算
    xs = map(lambda data: data[0], ls)
    ys = map(lambda data: data[1], ls)
    zs = map(lambda data: data[2], ls)
    guoqiao
        11
    guoqiao  
       2021-10-07 10:40:02 +08:00
    @ch2 你这把一个循环变成了三个, 应该只会更慢吧
    WhoMercy
        12
    WhoMercy  
       2021-10-07 11:13:02 +08:00
    O(n)了还优化个啥
    fancy967
        13
    fancy967  
       2021-10-07 12:00:32 +08:00   ❤️ 1
    不知道怎么优化,不过代码可以精简一下
    for _x, _y, _z in ls:
    xs.append(_x)
    ys.append(_y)
    zs.append(_z)
    dangyuluo
        14
    dangyuluo  
       2021-10-07 12:02:14 +08:00
    C++程序员的思路:预先分配下内存防止移动?
    cyrbuzz
        15
    cyrbuzz  
       2021-10-07 12:39:46 +08:00   ❤️ 1
    你要全部遍历一遍,这个算法已经 O(n),除了直接提速 for 和 append,可以用另外一种思路,就是看你的 xs,ys,zs 的用处,用 yield 把它改成生成器,类似 python2 里 range 到 xrange 的改变。

    如果 ls 不变,进一步的优化可以加缓存,用 JSON 存到本地,第二次直接读取 JSON,虽然本身并没有优化到算法。
    MintZX
        16
    MintZX  
       2021-10-07 13:18:44 +08:00
    @zacharyjia 因为这个操作在 numpy 里面是 constant 的。。通过数据结构实现的。这也就是为什么你把 list 转成 np 的时间非常高的原因。当然了,你也可以试试看把处理好的 xs ys zs 再转成 list,还是很费时间。

    np 本身的 dataframe 非常复杂也非常大
    niubee1
        17
    niubee1  
       2021-10-07 13:25:51 +08:00   ❤️ 10
    其实是一个 90 度旋转二维数组的过程,用 Python 的内置函数实现应该会更快,因为毕竟底层是 C 。
    可以先 rotated = list(zip(*ls[::])) 旋转一下二维数组,再 xs.extend(rotated) .

    跑起来大概提高了一倍的速度

    https://imgur.com/e602lpH
    NoAnyLove
        18
    NoAnyLove  
       2021-10-07 13:28:28 +08:00
    @guoqiao Python 的优化就是有些反常识,3 个循环不一定比 1 个循环慢,具体还是看解释器怎么跑
    @dangyuluo 预分配内存在 Python 优化中通常效果不佳,因为通常不是主要因素

    如果 ls 足够长的话,比如 ls=[[i, i+1, i+2] for i in range(1, 98, 3)],那么这个版本可能更快一些,

    flat = list(itertools.chain.from_iterable(ls))
    xs = flat[::3]
    ys = flat[1::3]
    zs = flat[2::3]
    niubee1
        19
    niubee1  
       2021-10-07 13:28:37 +08:00   ❤️ 6
    NoAnyLove
        20
    NoAnyLove  
       2021-10-07 13:32:13 +08:00
    好吧,#17 我服了
    niubee1
        21
    niubee1  
       2021-10-07 13:35:14 +08:00
    @NoAnyLove 我测了一下,zip 稍快一些
    niubee1
        22
    niubee1  
       2021-10-07 13:36:30 +08:00
    niubee1
        23
    niubee1  
       2021-10-07 13:37:15 +08:00   ❤️ 2
    NoAnyLove
        24
    NoAnyLove  
       2021-10-07 13:41:01 +08:00
    @niubee1 同意,你的版本更快,

    In [101]: def t1(ls):
    ...: flat = list(itertools.chain.from_iterable(ls))
    ...: xs = flat[::3]
    ...: ys = flat[1::3]
    ...: zs = flat[2::3]
    ...: return xs, ys, zs
    ...:

    In [102]: def t2(ls):
    ...: xs, ys, zs =list(zip(*ls))
    ...: return list(xs), list(ys), list(zs)

    In [113]: ls=[[i, i+1, i+2] for i in range(1, 98, 3)]

    In [114]: %timeit t1(ls)
    4.26 µs ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

    In [115]: %timeit t2(ls)
    3.2 µs ± 19.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    niubee1
        25
    niubee1  
       2021-10-07 13:43:13 +08:00
    其实省掉 extend 过程,直接返回数组的话,还能再提高点性能
    niubee1
        26
    niubee1  
       2021-10-07 13:45:12 +08:00   ❤️ 2
    @NoAnyLove 🤝 其实大家思路都差不多,python 要提高效率的话,应该尽可能的使用内置函数
    NoAnyLove
        27
    NoAnyLove  
       2021-10-07 13:57:43 +08:00
    @niubee1 对,就是这个道理,内置函数底层执行更快 🤝
    shyrock
        28
    shyrock  
       2021-10-07 16:40:25 +08:00
    @niubee1 #19 学习了。
    wuwukai007
        29
    wuwukai007  
       2021-10-08 11:13:56 +08:00
    #19 楼 一行代码开启新世界
    princelai
        30
    princelai  
       2021-10-08 18:14:49 +08:00
    ```python
    def func4(ls):
    xs = list(islice(chain.from_iterable(ls), 0, None, 3))
    ys = list(islice(chain.from_iterable(ls), 1, None, 3))
    zs = list(islice(chain.from_iterable(ls), 2, None, 3))


    def func5(ls):
    xs = list(compress(chain.from_iterable(ls), cycle([1, 0, 0])))
    ys = list(compress(chain.from_iterable(ls), cycle([0, 1, 0])))
    zs = list(compress(chain.from_iterable(ls), cycle([0, 0, 1])))
    ```
    itertools 里的内置函数速度都还可以
    rationa1cuzz
        31
    rationa1cuzz  
       2021-10-09 09:39:44 +08:00
    @niubee1 ls[::] 是干嘛?我怎么看不懂啊,求教,另外为什么我测的是列表推导式更快一点,数量级越大越明显
    rationa1cuzz
        32
    rationa1cuzz  
       2021-10-09 09:48:52 +08:00
    @niubee1 另外,只有在 data=[(x,y,z),(x2,y2,z2,...)] 为元祖 zip 才会有明显速度优势,
    data[(x,y,z),(x2,y2,z2,...)] range(100000)
    for: spend_time:0.03163599967956543
    列表推导式:spend_time:0.012620925903320312
    zip: spend_time:0.0060007572174072266

    data[(x,y,z),(x2,y2,z2,...)] range(100000)
    for: spend_time:0.03195595741271973
    列表推导式:spend_time:0.012039899826049805
    zip: spend_time:0.016546964645385742
    htaoreg
        33
    htaoreg  
       2021-10-10 11:38:52 +08:00
    xs, ys, zs = zip(*ls)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2759 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 12:11 · PVG 20:11 · LAX 05:11 · JFK 08:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.