提高 Pandas DataFrame 上的行追加性能
Improve Row Append Performance On Pandas DataFrames
我是 运行 一个循环遍历嵌套字典、从每条记录中获取数据并将其附加到 Pandas DataFrame 的基本脚本。数据看起来像这样:
data = {"SomeCity": {"Date1": {record1, record2, record3, ...}, "Date2": {}, ...}, ...}
总共有几百万条记录。脚本本身如下所示:
city = ["SomeCity"]
df = DataFrame({}, columns=['Date', 'HouseID', 'Price'])
for city in cities:
for dateRun in data[city]:
for record in data[city][dateRun]:
recSeries = Series([record['Timestamp'],
record['Id'],
record['Price']],
index = ['Date', 'HouseID', 'Price'])
FredDF = FredDF.append(recSeries, ignore_index=True)
但是,这运行起来非常慢。在我寻找一种并行化方法之前,我只是想确保我没有遗漏一些明显的东西,这些东西会使它按原样执行得更快,因为我对 Pandas.[=13 还是很陌生=]
我 运行 遇到了一个类似的问题,我不得不多次追加到 DataFrame,但不知道追加之前的值。我写了一个类似数据结构的轻量级 DataFrame,它只是底层的 blists()。我用它来累积所有数据,然后当它完成时 t运行sform 输出到 Pandas DataFrame。这是我项目的 link,全部开源,希望对其他人有所帮助:
我还在循环中使用了数据帧的 append 函数,我很困惑它有多慢 运行。
根据本页上的正确答案,为遭受苦难的人提供了一个有用的示例。
Python版本:3
Pandas版本:0.20.3
# the dictionary to pass to pandas dataframe
d = {}
# a counter to use to add entries to "dict"
i = 0
# Example data to loop and append to a dataframe
data = [{"foo": "foo_val_1", "bar": "bar_val_1"},
{"foo": "foo_val_2", "bar": "bar_val_2"}]
# the loop
for entry in data:
# add a dictionary entry to the final dictionary
d[i] = {"col_1_title": entry['foo'], "col_2_title": entry['bar']}
# increment the counter
i = i + 1
# create the dataframe using 'from_dict'
# important to set the 'orient' parameter to "index" to make the keys as rows
df = DataFrame.from_dict(d, "index")
“from_dict”函数:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.from_dict.html
将行附加到列表比添加到 DataFrame
更有效。
因此你会想要
- 将行追加到列表中。
- 然后转换成
DataFrame
和
- 根据需要设置索引。
我认为最好的方法是,如果您知道要接收的数据,请事先分配。
import numpy as np
import pandas as pd
random_matrix = np.random.randn(100, 100)
insert_df = pd.DataFrame(random_matrix)
df = pd.DataFrame(columns=range(100), index=range(200))
df.loc[range(100), df.columns] = random_matrix
df.loc[range(100, 200), df.columns] = random_matrix
这是我认为最合理的模式。 append
如果
你有一个非常小的数据框,但它不能缩放。
In [1]: import numpy as np; import pandas as pd
In [2]: random_matrix = np.random.randn(100, 100)
...: insert_df = pd.DataFrame(random_matrix)
...: df = pd.DataFrame(np.random.randn(100, 100))
In [2]: %timeit df.append(insert_df)
272 µs ± 2.36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [3]: %timeit df.loc[range(100), df.columns] = random_matrix
493 µs ± 4.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [4]: %timeit df.loc[range(100), df.columns] = insert_df
821 µs ± 8.68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
当我们 运行 使用 100,000 行的数据帧时,我们会看到更加引人注目的结果。
In [1]: df = pd.DataFrame(np.random.randn(100_000, 100))
In [2]: %timeit df.append(insert_df)
17.9 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [3]: %timeit df.loc[range(100), df.columns] = random_matrix
465 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [4]: %timeit df.loc[range(99_900, 100_000), df.columns] = random_matrix
465 µs ± 5.75 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [5]: %timeit df.loc[range(99_900, 100_000), df.columns] = insert_df
1.02 ms ± 3.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
所以我们可以看到追加比使用数据帧插入慢 17 倍,比使用 numpy 数组插入慢 35 倍。
另一种方法是将其制成列表,然后使用pd.concat
import pandas as pd
df = pd.DataFrame({'num_legs': [2, 4, 8, 0],
'num_wings': [2, 0, 0, 0],
'num_specimen_seen': [10, 2, 1, 8]},
index=['falcon', 'dog', 'spider', 'fish'])
def append(df):
df_out = df.copy()
for i in range(1000):
df_out = df_out.append(df)
return df_out
def concat(df):
df_list = []
for i in range(1001):
df_list.append(df)
return pd.concat(df_list)
# some testing
df2 = concat(df)
df3 = append(df)
pd.testing.assert_frame_equal(df2,df3)
%timeit concat(df)
:
每个循环 20.2 ms ± 794 µs(7 次运行的平均值 ± 标准偏差,每次 100 次循环)
%timeit append(df)
每个循环 275 毫秒 ± 2.54 毫秒(7 次运行的平均值 ± 标准偏差,每次 1 个循环)
现在推荐使用 pandas 中的行连接方式:
Iteratively appending rows to a DataFrame can be more computationally intensive than a single concatenate. A better solution is to append those rows to a list and then concatenate the list with the original DataFrame all at once. link
在我的例子中,我从不同的文件中加载了大量具有相同列的数据框,并希望附加它们以创建一个大数据框。
我的解决方案是首先将所有数据帧加载到一个列表中,然后使用
all_dfs = []
for i in all_files:
all_dfs.append(/* load df from file */)
master_df = pd.concat(all_dfs, ignore_index=True)
我是 运行 一个循环遍历嵌套字典、从每条记录中获取数据并将其附加到 Pandas DataFrame 的基本脚本。数据看起来像这样:
data = {"SomeCity": {"Date1": {record1, record2, record3, ...}, "Date2": {}, ...}, ...}
总共有几百万条记录。脚本本身如下所示:
city = ["SomeCity"]
df = DataFrame({}, columns=['Date', 'HouseID', 'Price'])
for city in cities:
for dateRun in data[city]:
for record in data[city][dateRun]:
recSeries = Series([record['Timestamp'],
record['Id'],
record['Price']],
index = ['Date', 'HouseID', 'Price'])
FredDF = FredDF.append(recSeries, ignore_index=True)
但是,这运行起来非常慢。在我寻找一种并行化方法之前,我只是想确保我没有遗漏一些明显的东西,这些东西会使它按原样执行得更快,因为我对 Pandas.[=13 还是很陌生=]
我 运行 遇到了一个类似的问题,我不得不多次追加到 DataFrame,但不知道追加之前的值。我写了一个类似数据结构的轻量级 DataFrame,它只是底层的 blists()。我用它来累积所有数据,然后当它完成时 t运行sform 输出到 Pandas DataFrame。这是我项目的 link,全部开源,希望对其他人有所帮助:
我还在循环中使用了数据帧的 append 函数,我很困惑它有多慢 运行。
根据本页上的正确答案,为遭受苦难的人提供了一个有用的示例。
Python版本:3
Pandas版本:0.20.3
# the dictionary to pass to pandas dataframe
d = {}
# a counter to use to add entries to "dict"
i = 0
# Example data to loop and append to a dataframe
data = [{"foo": "foo_val_1", "bar": "bar_val_1"},
{"foo": "foo_val_2", "bar": "bar_val_2"}]
# the loop
for entry in data:
# add a dictionary entry to the final dictionary
d[i] = {"col_1_title": entry['foo'], "col_2_title": entry['bar']}
# increment the counter
i = i + 1
# create the dataframe using 'from_dict'
# important to set the 'orient' parameter to "index" to make the keys as rows
df = DataFrame.from_dict(d, "index")
“from_dict”函数:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.from_dict.html
将行附加到列表比添加到 DataFrame
更有效。
因此你会想要
- 将行追加到列表中。
- 然后转换成
DataFrame
和 - 根据需要设置索引。
我认为最好的方法是,如果您知道要接收的数据,请事先分配。
import numpy as np
import pandas as pd
random_matrix = np.random.randn(100, 100)
insert_df = pd.DataFrame(random_matrix)
df = pd.DataFrame(columns=range(100), index=range(200))
df.loc[range(100), df.columns] = random_matrix
df.loc[range(100, 200), df.columns] = random_matrix
这是我认为最合理的模式。 append
如果
你有一个非常小的数据框,但它不能缩放。
In [1]: import numpy as np; import pandas as pd
In [2]: random_matrix = np.random.randn(100, 100)
...: insert_df = pd.DataFrame(random_matrix)
...: df = pd.DataFrame(np.random.randn(100, 100))
In [2]: %timeit df.append(insert_df)
272 µs ± 2.36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [3]: %timeit df.loc[range(100), df.columns] = random_matrix
493 µs ± 4.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [4]: %timeit df.loc[range(100), df.columns] = insert_df
821 µs ± 8.68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
当我们 运行 使用 100,000 行的数据帧时,我们会看到更加引人注目的结果。
In [1]: df = pd.DataFrame(np.random.randn(100_000, 100))
In [2]: %timeit df.append(insert_df)
17.9 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [3]: %timeit df.loc[range(100), df.columns] = random_matrix
465 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [4]: %timeit df.loc[range(99_900, 100_000), df.columns] = random_matrix
465 µs ± 5.75 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [5]: %timeit df.loc[range(99_900, 100_000), df.columns] = insert_df
1.02 ms ± 3.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
所以我们可以看到追加比使用数据帧插入慢 17 倍,比使用 numpy 数组插入慢 35 倍。
另一种方法是将其制成列表,然后使用pd.concat
import pandas as pd
df = pd.DataFrame({'num_legs': [2, 4, 8, 0],
'num_wings': [2, 0, 0, 0],
'num_specimen_seen': [10, 2, 1, 8]},
index=['falcon', 'dog', 'spider', 'fish'])
def append(df):
df_out = df.copy()
for i in range(1000):
df_out = df_out.append(df)
return df_out
def concat(df):
df_list = []
for i in range(1001):
df_list.append(df)
return pd.concat(df_list)
# some testing
df2 = concat(df)
df3 = append(df)
pd.testing.assert_frame_equal(df2,df3)
%timeit concat(df)
:
每个循环 20.2 ms ± 794 µs(7 次运行的平均值 ± 标准偏差,每次 100 次循环)
%timeit append(df)
每个循环 275 毫秒 ± 2.54 毫秒(7 次运行的平均值 ± 标准偏差,每次 1 个循环)
现在推荐使用 pandas 中的行连接方式:
Iteratively appending rows to a DataFrame can be more computationally intensive than a single concatenate. A better solution is to append those rows to a list and then concatenate the list with the original DataFrame all at once. link
在我的例子中,我从不同的文件中加载了大量具有相同列的数据框,并希望附加它们以创建一个大数据框。
我的解决方案是首先将所有数据帧加载到一个列表中,然后使用
all_dfs = []
for i in all_files:
all_dfs.append(/* load df from file */)
master_df = pd.concat(all_dfs, ignore_index=True)