提问人:user6703592 提问时间:7/12/2023 最后编辑:user6703592 更新时间:7/12/2023 访问量:99
Python 数组体积大,时间成本高
Python large size array and high time cost
问:
我想创建一个作为机器学习模型的输入:np.ndarray
array_X = np.array([list(w.values) for w in df[['close', 'volume']].rolling(window=20)][19:-1])
这是时间序列中的标准方法,我们使用过去值的窗口作为输入来预测未来值。数组的形状是 。构建这样的数组会花费很多时间,有时还会出现数组消耗的内存过大的错误。2 * 20 * 20000000
有什么方法可以改善上述问题(时间成本和内存错误)吗?
答:
3赞
Sebastian Wozny
7/12/2023
#1
您的原始代码给了我一个错误,因为数组中前几个条目的维度不匹配,这些条目太短了,因为窗口尚未满,因此我将其更改为丢弃第一个值:
def rolling_approach(df, window_size=3):
return np.array(
[w.values for w in df[["close", "volume"]].rolling(window=window_size)][
window_size - 1 :
]
)
PD的。对于这类操作,DataFrame.rolling
可能非常慢。shift
非常有效 下面是一个写出的例子:pandas
window_size=3
pd.concat(
[
df[["close", "volume"]].shift().shift(),
df[["close", "volume"]].shift(),
df[["close", "volume"]],
],
axis=1,
)
.values[2:, :]
.reshape(-1, 3, 2)
)
我们堆叠班次,然后重塑值。
推广到一个变量,我们得到:window_size
def shift_approach(df, window_size=3):
shifted_df = pd.concat(
[df[["close", "volume"]].shift(i) for i in range(window_size - 1, -1, -1)],
axis=1,
)
reshaped_array = shifted_df.values[window_size - 1 :, :].reshape(-1, window_size, 2)
return reshaped_array
shift
性能比操作高出近两个数量级:rolling
def setup(N):
np.random.seed(42)
close_values = np.random.randint(1, 100, size=N)
volume_values = np.random.randint(100, 1000, size=N)
df = pd.DataFrame({"close": close_values, "volume": volume_values})
return [df, 10]
approaches = [rolling_approach, shift_approach]
# Show correctness
for approach in approaches[1:]:
data = setup(10)
assert np.isclose(approach(*data), approaches[0](*data)).all()
run_performance_comparison(
approaches,
[
1000,
3000,
5000,
10000,
30000,
100_000,
300_000,
500_000,
1_000_000,
3_000_000,
5_000_000,
10_000_000,
],
setup=setup,
title="Performance Comparison",
number_of_repetitions=2,
)
分析代码:
import timeit
from functools import partial
import matplotlib.pyplot as plt
from typing import List, Dict, Callable
from contextlib import contextmanager
@contextmanager
def data_provider(data_size, setup=lambda N: N, teardown=lambda: None):
data = setup(data_size)
yield data
teardown()
def run_performance_comparison(approaches: List[Callable],
data_size: List[int],
setup=lambda N: N,
teardown=lambda: None,
number_of_repetitions=5, title='Performance Comparison', data_name='N'):
approach_times: Dict[Callable, List[float]] = {approach: [] for approach in approaches}
for N in data_size:
with data_provider(N, setup, teardown) as data:
for approach in approaches:
function = partial(approach, *data)
approach_time = min(timeit.Timer(function).repeat(repeat=number_of_repetitions, number=2))
approach_times[approach].append(approach_time)
for approach in approaches:
plt.plot(data_size, approach_times[approach], label=approach.__name__)
plt.yscale('log')
plt.xscale('log')
plt.xlabel(data_name)
plt.ylabel('Execution Time (seconds)')
plt.title(title)
plt.legend()
plt.show()
评论
0赞
user6703592
7/13/2023
我试过你的解决方案要快得多!所以剩下的问题是,大容量的数组似乎有一个内存错误,如 MemoryError: Unable to assign 84.4 MiB for an array with a shape (11056593,)
0赞
dankal444
7/13/2023
@user6703592可能会使用 np.float32(如果可能的话,甚至可以使用更小的数据类型)。
0赞
user6703592
7/13/2023
@dankal444事情很奇怪,有时从 df.values 生成的相同数组是可以的,但是数组从其他方式构建,例如 for 循环:array_X = [] for i in range(): array_X.append()...将出现内存错误。是否与临时缓存的某些溢出有关?
0赞
Sebastian Wozny
7/13/2023
分析内存可能非常棘手。这取决于你的平台、程序的其余部分以及 Python 在任何特定时间点想要做什么。我将解决方案扩展到几亿行,并且在我的 macbook 上没有收到 OOM 错误。也许在您的程序中撒上几个电话?可能什么都不做,但你永远不知道。gc.collect()
0赞
user6703592
8/24/2023
我在这里问了一个类似的问题:stackoverflow.com/questions/76965427/......,你有什么想法吗?
评论