改进Pundas DataFrame上的行附加性能

Bri*_*eau 16 python numpy python-2.7 pandas

我正在运行一个循环嵌套字典的基本脚本,从每个记录中抓取数据,并将其附加到Pandas DataFrame.数据看起来像这样:

data = {"SomeCity": {"Date1": {record1, record2, record3, ...}, "Date2": {}, ...}, ...}
Run Code Online (Sandbox Code Playgroud)

总共有几百万条记录.脚本本身看起来像这样:

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)
Run Code Online (Sandbox Code Playgroud)

然而,这种情况非常缓慢.在我寻找一种并行化的方法之前,我只是想确保我没有遗漏一些明显会让它表现得更快的东西,因为我对Pandas来说还是一个新手.

Bri*_*eau 13

使用BrenBarn的建议,我简单地将原始字典重组为一个新的字典,该字典被正确格式化以利用from_dict的预期结构.字典的重组很快完成,然后只是用这个新字典调用from_dict.

整个过程在大约12秒内从数据加载到数据写入,而原始小时左右.好多了!


P-S*_*P-S 10

我还在循环中使用了数据框的append函数,感到困惑的是它的运行速度如何。

根据此页面上的正确答案,为遭受苦难的人提供有用的示例。

Python版本:3

熊猫版:0.20.3

# the dictionary to pass to panda's dataframe
dict = {}

# 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
    dict[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(dict, "index")
Run Code Online (Sandbox Code Playgroud)

“ from_dict”函数:https ://pandas.pydata.org/pandas-docs/stable/generation/pandas.DataFrame.from_dict.html

  • 这个例子绝对很有帮助! (3认同)
  • 这真的很快。大约需要 20 秒的操作现在可以在几毫秒内完成。万分感谢 :) (3认同)
  • 这肯定是一种快速的方法,但由于 Python 的默认字典不是 excel 中的有序数据,因此可能会随机混合。我强烈建议使用集合中的 OrderedDict 库。 (2认同)
  • 很棒的提示。非常有用。对于我的用例,通过使用此方法,我将时间从 45 分钟以上减少到了 5 分钟以下。 (2认同)

小智 9

另一种方法是将其放入列表中,然后使用pd.concat

\n\n
import pandas as pd \n\ndf = pd.DataFrame({\'num_legs\': [2, 4, 8, 0],\n\n                   \'num_wings\': [2, 0, 0, 0],\n\n                   \'num_specimen_seen\': [10, 2, 1, 8]},\n\n                  index=[\'falcon\', \'dog\', \'spider\', \'fish\'])\n\ndef append(df):\n    df_out = df.copy()\n    for i in range(1000):\n        df_out = df_out.append(df)\n    return df_out\n\ndef concat(df):\n    df_list = []\n    for i in range(1001):\n        df_list.append(df)\n\n    return pd.concat(df_list)\n\n\n# some testing\ndf2 = concat(df)\ndf3 = append(df)\n\npd.testing.assert_frame_equal(df2,df3)\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

%timeit concat(df)

\n\n

每个循环 20.2 ms \xc2\xb1 794 \xc2\xb5s(意味着 7 次运行的 \xc2\xb1 标准偏差,每次 100 个循环)

\n\n

%timeit append(df)

\n\n

275 ms \xc2\xb1 每个循环 2.54 ms(平均 \xc2\xb1 标准偏差 7 次运行,每次 1 次循环)

\n\n

现在推荐使用 pandas 来连接行:

\n\n
\n

以迭代方式将行追加到 DataFrame 中的计算量可能比单个连接的计算量更大。更好的解决方案是将这些行追加到列表中,然后立即将该列表与原始 DataFrame 连接起来。关联

\n
\n


小智 7

将行附加到列表比附加到DataFrame. 因此你会想要

  1. 将行附加到列表中。
  2. 然后将其转换为 DataFrame
  3. 根据需要设置索引。


Rya*_*tel 5

我遇到了类似的问题,我必须多次附加到 DataFrame,但在附加之前不知道值。我编写了一个类似 DataFrame 的轻量级数据结构,其底层就是 blists() 。我用它来累积所有数据,然后在完成后将输出转换为 Pandas DataFrame。这是我的项目的链接,全部开源,所以我希望它对其他人有帮助:

https://pypi.python.org/pypi/raccoon


Rob*_*Rob 5

我认为最好的方法是,如果您知道要接收的数据,请事先分配。

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
Run Code Online (Sandbox Code Playgroud)

这是我认为最有意义的模式。 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)
Run Code Online (Sandbox Code Playgroud)

当我们使用 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)
Run Code Online (Sandbox Code Playgroud)

所以我们可以看到追加比插入数据帧慢 17 倍,比插入 numpy 数组慢 35 倍。