我想从字典中列出的每个类别的所有可能的值组合中创建一个数据框架。
我尝试了下面的代码,对于具有较少键和值的小字典,它工作得很好。但是对于我下面给出的更大的字典,它不会被执行。
import itertools as it
import pandas as pd
my_dict= {
"A":[0,1,.....25],
"B":[4,5,.....35],
"C":[0,1,......30],
"D":[0,1,........35],
.........
"Y":[0,1,........35],
"Z":[0,1,........35],
}
df=pd.DataFrame(list(it.product(*my_dict.values())), columns=my_dict.keys())
这是我得到的错误,如何处理大字典的问题
Traceback (most recent call last):
File "<ipython-input-11-723405257e95>", line 1, in <module>
df=pd.DataFrame(list(it.product(*my_dict.values())), columns=my_dict.keys())
MemoryError
如何处理用大字典创建数据帧
如果你有一个足够大的[1]Spark集群,字典中的每个列表都可以用作一个Spark数据框,然后所有这些数据框可以交叉连接:
def to_spark_dfs(dict):
for key in dict:
l=[[e] for e in dict[key]]
yield spark.createDataFrame(l, schema=[key])
dfs=to_spark_dfs(my_dict)
from functools import reduce
res=reduce(lambda df1,df2: df1.crossJoin(df2),dfs)
如果原my_dict
不太大
my_dict= {
"A":[0,1,2],
"B":[4,5,6],
"C":[0,1,2],
"D":[0,1],
"Y":[0,1,2],
"Z":[0,1],
}
代码产生预期的结果:
res.show()
#+---+---+---+---+---+---+
#| A| B| C| D| Y| Z|
#+---+---+---+---+---+---+
#| 0| 4| 0| 0| 0| 0|
#| 0| 4| 0| 0| 0| 1|
#| 0| 4| 0| 0| 1| 0|
#| 0| 4| 0| 0| 1| 1|
#...
res.count()
#324
[1]使用评论中给出的数字(80个键和每个键大约30个值),您将需要一个真正大的Spark集群:30 ^ 80
给1.5*10^118
不同的组合。这比已知的可观测宇宙中估计的原子数(10^80
)还要多。
在这种情况下,我们有大量的可能的组合。例如,如果列(A, B, C…)Z)可以取值[1…[10]则总行数等于10^26,即1000000000000000000000000000000。
在我看来,有两个主要的方向来解决这个问题:
-
水平扩展:使用分布式计算框架(如
Apache Spark
或Hadoop
)计算和存储结果 -
垂直扩展优化CPU/RAM利用率
- 矢量化(例如避免
loops
) - 对RAM分配影响最小的数据类型(使用尽可能小的精度,对字符串使用
factorize()
) - 迷你批处理并以压缩格式(例如
parquet
)从RAM下载中间结果(数据帧)到磁盘 - 对内存中的执行时间和对象大小进行基准测试。
- 矢量化(例如避免
让我介绍一下实现垂直缩放方法的一些概念的代码。
定义以下函数:
create_data_frame_baseline()
:数据帧生成器与循环,不是最优的数据类型(基线)create_data_frame_no_loop()
:无循环,非最佳数据类型create_data_frame_optimize_data_type()
:无循环,最优数据类型。
import itertools as it
import pandas as pd
import numpy as np
from string import ascii_uppercase
def create_letter_dict(cols_n: int = 10, levels_n: int = 6) -> dict:
letter_dict = {letter: list(range(levels_n)) for letter in ascii_uppercase[0:cols_n]}
return letter_dict
def create_data_frame_baseline(dict: dict) -> pd.DataFrame:
df = pd.DataFrame(columns=dict.keys())
for row in it.product(*dict.values()):
df.loc[len(df.index)] = row
return df
def create_data_frame_no_loop(dict: dict) -> pd.DataFrame:
return pd.DataFrame(
list(it.product(*dict.values())),
columns=dict.keys()
)
def create_data_frame_optimize_data_type(dict: dict) -> pd.DataFrame:
return pd.DataFrame(
np.int8(list(it.product(*dict.values()))),
columns=dict.keys()
)
基准:
import sys
import timeit
cols_n = 7
levels_n = 5
iteration_n = 2
# Baseline
def create_data_frame_baseline_test():
my_dict = create_letter_dict(cols_n, levels_n)
df = create_data_frame_baseline(my_dict)
assert(df.shape == (levels_n**cols_n, cols_n))
print(sys.getsizeof(df))
return df
print(timeit.Timer(create_data_frame_baseline_test).timeit(number=iteration_n))
# No loop, not optimal data types
def create_data_frame_no_loop_test():
my_dict = create_letter_dict(cols_n, levels_n)
df = create_data_frame_no_loop(my_dict)
assert(df.shape == (levels_n**cols_n, cols_n))
print(sys.getsizeof(df))
return df
print(timeit.Timer(create_data_frame_no_loop_test).timeit(number=iteration_n))
# No loop, optimal data types.
def create_data_frame_optimize_data_type_test():
my_dict = create_letter_dict(cols_n, levels_n)
df = create_data_frame_optimize_data_type(my_dict)
assert(df.shape == (levels_n**cols_n, cols_n))
print(sys.getsizeof(df))
return df
print(timeit.Timer(create_data_frame_optimize_data_type_test).timeit(number=iteration_n))
输出*:
函数 | 数据帧形状 | 内存大小,Mb | 执行时间,sec | create_data_frame_baseline_test | 78125 x7 | 19 | 485 |
---|---|---|---|
create_data_frame_no_loop_test | 78125 x7 | 4.4 | 0.20 |
create_data_frame_optimize_data_type_test | 78125 x7 | 0.55 | 0.16 |
在您的情况下,您不能一次生成所有可能的组合,通过使用list()
,但在循环中进行,例如:
import itertools as it
import pandas as pd
from string import ascii_uppercase
N = 36
my_dict = {x: list(range(N)) for x in ascii_uppercase}
df = pd.DataFrame(columns=my_dict.keys())
for row in it.product(*my_dict.values()):
df.loc[len(df.index)] = row
但这需要很长时间