【问题标题】:Fast method to create nested list with different types: numpy, pandas or list concatenation?创建具有不同类型的嵌套列表的快速方法:numpy、pandas 或列表连接?
【发布时间】:2020-04-24 04:18:02
【问题描述】:

我正在尝试加速下面的代码,该代码会为每列生成一个具有不同类型的列表列表。我最初创建了 pandas 数据框,然后将其转换为列表,但这似乎相当慢。我怎样才能更快地创建这个列表,比如说一个数量级?除了一列之外,所有列都是不变的。

import pandas as pd
import numpy as np
import time
import datetime

def overflow_check(x):
    # in SQL code the column is decimal(13, 2)
    p=13
    s=3
    max_limit = float("9"*(p-s) + "." + "9"*s)
    #min_limit =  0.01 #float("0" + "." + "0"*(s-2) + '1')
    #min_limit = 0.1
    if np.logical_not(isinstance(x, np.ndarray)) or len(x) < 1:
        raise Exception("Non-numeric or empty array.")
    else:
        #print(x)
        return x * (np.abs(x) < max_limit) + np.sign(x)* max_limit * (np.abs(x) >= max_limit)

def list_creation(y_forc):


    backcast_length = len(y_forc)

    backcast = pd.DataFrame(data=np.full(backcast_length, 2),
                            columns=['TypeId'])


    backcast['id2'] = None
    backcast['Daily'] = 1
    backcast['ForecastDate'] = y_forc.index.strftime('%Y-%m-%d')
    backcast['ReportDate'] = pd.to_datetime('today').strftime('%Y-%m-%d')
    backcast['ForecastMethodId'] = 1
    backcast['ForecastVolume'] = overflow_check(y_forc.values)
    backcast['CreatedBy'] = 'test'
    backcast['CreatedDt'] = pd.to_datetime('today')


    return backcast.values.tolist()

i=pd.date_range('05-01-2010', '21-05-2018', freq='D')
x=pd.DataFrame(index=i, data = np.random.randint(0, 100, len(i)))

t=time.perf_counter()
y =list_creation(x)
print(time.perf_counter()-t)

【问题讨论】:

  • 我建议返回 backcast 数据框,然后应用 values.tolist()。这样你就可以更好地测试瓶颈在哪里。我对pandas 没有足够的经验来判断什么是慢的。但似乎您一次创建一个框架Series,这似乎是合理的。直接构建列表需要迭代整个时间,一次构建一个子列表。我猜这将很难扩展。
  • 混合使用 dtypes(数据框列),backcast.to_records().tolist() 转换可能会让您更满意,但这只是猜测。
  • @hpaulj 在性能上似乎相似。我猜是一一列添加导致了这个问题。

标签: python python-3.x pandas list numpy


【解决方案1】:

这应该会快一点,它只是直接创建列表:

def list_creation1(y_forc):
    zipped = zip(y_forc.index.strftime('%Y-%m-%d'), overflow_check(y_forc.values)[:,0])
    t = pd.to_datetime('today').strftime('%Y-%m-%d')
    t1 =pd.to_datetime('today')
    return [
        [2, None, 1, i, t,
        1, v, 'test', t1] 
        for i,v in zipped
    ]


%%timeit
list_creation(x)
> 29.3 ms ± 468 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
list_creation1(x)
> 17.1 ms ± 517 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

编辑:缓慢的一大问题是从日期时间到指定格式所需的时间。如果我们可以通过如下表述来摆脱它:

def list_creation1(i, v):
    zipped = zip(i, overflow_check(np.array([[_x] for _x in v]))[:,0])
    t = pd.to_datetime('today').strftime('%Y-%m-%d')
    t1 =pd.to_datetime('today')
    return [
        [2, None, 1, i, t,
        1, v, 'test', t1] 
        for i,v in zipped
    ]

start = datetime.datetime.strptime("05-01-2010", "%d-%m-%Y")
end = datetime.datetime.strptime("21-05-2018", "%d-%m-%Y")
i = [(start + datetime.timedelta(days=x)).strftime("%d-%m-%Y") for x in range(0, (end-start).days)]
x=np.random.randint(0, 100, len(i))

那么现在速度要快很多:

%%timeit
list_creation1(i, x)
> 1.87 ms ± 24.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【讨论】:

  • 谢谢,@hchw,我想知道是否可以使用 numpy 结构或类似的东西来完成某些事情。我曾经求助于 numpy 数组而不是数据帧,并且代码加速了 2 个数量级。但是这里我有不同的类型,所以不知道如何进行。快 50% 似乎没有多大帮助。
  • 已编辑...此时这与数据帧没有太大关系
  • 但这不是更快,因为慢操作被推到函数定义之外吗?我没有明确说明的另一点是数据帧 x 的日期时间索引可能不是周期性的,而是到处都有随机间隙。
  • 我的观点是,你的函数的缓慢与 pandas 无关(在转换为列表之后),而是几乎完全是你必须支付的从 datetime 到 strfttime 的成本
  • 另外,这确实加快了速度,因为对日期的列表理解比 df.index.strfttime() 快...如果你把它放在函数内,时间是 ~12(仍然不是随心所欲,但现在这是一个单独的问题)
猜你喜欢
  • 2022-11-24
  • 1970-01-01
  • 2020-03-30
  • 1970-01-01
  • 2013-05-23
  • 1970-01-01
  • 2022-10-07
  • 1970-01-01
  • 2022-08-19
相关资源
最近更新 更多