【问题标题】:Fastest way to loop over Pandas DataFrame for API calls循环 Pandas DataFrame 以进行 API 调用的最快方法
【发布时间】:2017-10-17 21:07:22
【问题描述】:

我的目标是为 Pandas DataFrame 中的每一行调用一个 API,其中包含响应 JSON 中的字符串列表,并创建一个新的 DataFrame,每个响应一行。我的代码基本上是这样的:

i = 0
new_df = pandas.DataFrame(columns = ['a','b','c','d'])
for index,row in df.iterrows():
    url = 'http://myAPI/'
    d = '{"SomeJSONData:"' + row['data'] + '}'
    j = json.loads(d)
    response = requests.post(url,json = j)

    data = response.json()
    for new_data in data['c']:
        new_df.loc[i] = [row['a'],row['b'],row['c'],new_data]
        i += 1

这工作正常,但我正在进行大约 5500 次 API 调用并将大约 6500 行写入新的 DataFrame,因此需要一段时间,可能需要 10 分钟。我想知道是否有人知道加快速度的方法?我对在 Python 中运行并行 for 循环不太熟悉,这可以在保持线程安全的同时完成吗?

【问题讨论】:

  • 由于您的数据框有多个 dtype=object 列,因此仅使用 iterrows 将尽可能快。您可以尝试线程,因为请求是 i/o 绑定的。在这种情况下,请查看示例 here。请注意,这个问题最初是在 7 年前提出的。所以看看最近的例子。
  • 我会在 pandas 之外使用 requests-futures 来发出异步请求,获取结果,然后重建列。
  • 问题不在于循环。这是api调用。我不知道 web api 是否可以处理批量查询。我不是 @roganjosh 建议的专家……但这听起来是个好主意。
  • 另外,你应该考虑不要使用requests,因为它是同步设计的。查看一些选项here

标签: python pandas python-requests


【解决方案1】:

也许是这样的?这样您就不会创建一个全新的数据框,您只需声明一次 URL,并且您正在利用 pandas 列操作比逐行操作更快的事实。

url = 'http://myAPI/'

def request_function(j):
    return requests.post(url,json = json.loads(j))['c'] 

df['j']= '{"SomeJsonData:"' + df['data'] + '}'
df['new_data'] = df['j'].apply(request_function)

现在证明在这种情况下使用 apply ( String data )确实快得多,这里有一个简单的测试:

import numpy as np
import pandas as pd
import time

def func(text):
    return text + ' is processed'


def test_one():
    data =pd.DataFrame(columns = ['text'], index = np.arange(0, 100000))
    data['text'] = 'text'

    start = time.time()
    data['text'] = data['text'].apply(func)
    print(time.time() - start)


def test_two():
    data =pd.DataFrame(columns = ['text'], index = np.arange(0, 100000))
    data['text'] = 'text'

    start = time.time()

    for index, row in data.iterrows():
        data.loc[index, 'text'] = row['text'] + ' is processed'

    print(time.time() - start)

对数据帧的字符串操作的结果。

test_one(使用应用):0.023002147674560547

test_two(使用 iterrows):18.912891149520874

基本上,通过使用添加两列和应用的内置pandas操作,您应该会获得更快的结果,您的响应时间确实受到API响应时间的限制。如果结果仍然太慢,您可能会考虑编写一个将结果保存到列表的异步函数。然后你 send.apply 那个异步函数。

【讨论】:

  • .apply 通常比 for 循环快。实际上,它在底层是一个 Python for 循环
  • @juanpa.arrivillaga 请不要传播错误信息。 Pandas 在幕后执行了许多优化。 apply 函数最终利用了许多内部优化,例如在 Cython 中使用迭代器。如果您不同意这一点,请尝试自己进行测试。统计数据胜于雄辩。
  • 真正的 OP 可以向 .apply 函数发送一个异步函数,该函数将结果附加到磁盘上的文件中,或者将其添加到列表中,然后快如闪电。
  • 你是对的,我在想pandas.Dataframe.applyaxis=1,据我所知,它确实恢复到python for-loop(即将到来与 iterrows 几乎同时出现)。我实际上非常惊讶pd.Series.apply 使用字符串操作的速度有多快。深入挖掘,它似乎实际上是基于loc 的字符串分配,它使所有事情都出错了。换句话说,应用该函数,并使用itertuples 迭代数据帧会得到大约0.10 秒。仍然较慢,但不是 2 个数量级
  • 是的,但正如您所见,实际上并不是.applyitertuples,而是基于ilocassignment 减慢了速度。因此,如果您修改测试 2 以附加到列表,然后使用 data['text'] = accumulator_list,则性能差异约为 0.90.5 在我的机器上(而 iloc 分配约为 20 秒)。这样做df.iloc[x, y] = z 的巨大惩罚对我来说完全是一个惊喜,但我基本上从不这样做。
猜你喜欢
  • 2019-05-25
  • 1970-01-01
  • 2019-04-10
  • 2019-06-09
  • 1970-01-01
  • 2017-12-11
  • 2016-01-14
  • 2017-11-28
  • 2017-08-19
相关资源
最近更新 更多