【问题标题】:How can I load a Plaid banking API response to a pandas dataframe in python?如何在 python 中将 Plaid 银行 API 响应加载到 pandas 数据框?
【发布时间】:2021-12-05 20:36:02
【问题描述】:

我正在使用 Plaid 的 API 返回银行账户的余额。他们的文档表明所有响应都采用标准 JSON。我有从请求模块加载 JSON 响应的经验,但我无法直接将 Plaid 的响应加载到 pandas 数据框。当我尝试时会发生以下情况:

request = AccountsBalanceGetRequest(access_token=token)
response = client.accounts_balance_get(request)
df = pd.json_normalize(response, record_path=['accounts'])

ERROR:
File "C:\Users\<me>\AppData\Local\Programs\Python\Python39\lib\site-packages\pandas\io\json\_normalize.py", line 423, in _json_normalize
    raise NotImplementedError

作为参考,print(response['accounts']) 正确访问响应的相关部分。这是错误中的 _normalize 部分,虽然我不明白如何应用它来解决问题:

    if isinstance(data, list) and not data:
        return DataFrame()
    elif isinstance(data, dict):
        # A bit of a hackjob
        data = [data]
    elif isinstance(data, abc.Iterable) and not isinstance(data, str):
        # GH35923 Fix pd.json_normalize to not skip the first element of a
        # generator input
        data = list(data)
    else:
        raise NotImplementedError

如果我打印响应,它看起来像这样:

{'accounts': [{'account_id': 'account_1',
               'balances': {'available': 300.0,
                            'current': 300.0,
                            'iso_currency_code': 'USD',
                            'limit': None,
                            'unofficial_currency_code': None},
               'mask': 'xxx1',
               'name': 'SAVINGS',
               'official_name': 'Bank Savings',
               'subtype': 'savings',
               'type': 'depository'},
              {'account_id': 'account_2',
               'balances': {'available': 500.00,
                            'current': 600.0,
                            'iso_currency_code': 'USD',
                            'limit': None,
                            'unofficial_currency_code': None},
               'mask': 'xxx2',
               'name': 'CHECKING',
               'official_name': 'Bank Checking',
               'subtype': 'checking',
               'type': 'depository'},
              {'account_id': 'account_3',
               'balances': {'available': 2000.00,
                            'current': 2000.00,
                            'iso_currency_code': 'USD',
                            'limit': None,
                            'unofficial_currency_code': None},
               'mask': 'xxx3',
               'name': 'BUSINESS CHECKING',
               'official_name': 'Bank Business Checking',
               'subtype': 'checking',
               'type': 'depository'}],
 'item': {'available_products': ['balance'],
          'billed_products': ['auth', 'transactions'],
          'consent_expiration_time': None,
          'error': None,
          'institution_id': 'ins_123xyz',
          'item_id': 'item_123xyz',
          'update_type': 'background',
          'webhook': ''},
 'request_id': 'request_123xyz'}

我假设如果 Plaid 的响应是标准 JSON,则单引号仅存在,因为 Python 的 print 将它们从双引号转换而来。如果我将此字符串作为基础并用双引号替换单引号,并将 None 替换为 "None",我可以加载到数据框:

data = json.loads(responseString.replace("'", '"').replace('None', '"None"'))
df = pd.json_normalize(data, record_path=['accounts'])
print(df)

将其直接应用于 Plaid 的响应也可以:

data = str(response)
data = data.replace("'", '"').replace('None', '"None"')
data = json.loads(data)
df = pd.json_normalize(data, record_path=['accounts'])

我所拥有的似乎是一个临时解决方案,但不是一个强大或预期的解决方案。有没有更优选的到达方式?

更新 1:本文第一个代码块的预期输出将生成以下数据帧,而不是错误:

 account_id  mask               name           official_name   subtype  ... balances.available  balances.current  balances.iso_currency_code balances.limit balances.unofficial_currency_code
0  account_1  xxx1            SAVINGS            Bank Savings   savings  ...              300.0             300.0                         USD           None                              None
1  account_2  xxx2           CHECKING           Bank Checking  checking  ...              500.0             600.0                         USD           None                              None
2  account_3  xxx3  BUSINESS CHECKING  Bank Business Checking  checking  ...             2000.0            2000.0                         USD           None                              None

我可以使用解决方法获得相同的输出,但不明白为什么它是必要的,而且依靠用双引号替换单引号似乎不是获得结果的好方法。

更新 2:我在 2021 年 10 月 15 日使用非 docker 指令和 npm 安装了格子组件。

print(plaid.__version__)
8.2.0
$ py --version
Python 3.9.6

更新 3:根据 Stephen 的建议答案添加完整的解决方案。响应需要首先显式转换为字典,然后从那里处理。什么有效:

json_string = json.loads(json.dumps(response.to_dict()))
df = pd.json_normalize(json_string, record_path=['accounts'])

这让我可以在转换为字符串后去掉所有需要的变通方法,基本上直接加载到数据帧。

【问题讨论】:

  • 请发布预期输出
  • 添加了更新以解决预期输出
  • 您能说明一下您使用的是哪个版本的格子 python 客户端库吗?谢谢!
  • 已更新版本信息

标签: python json pandas plaid


【解决方案1】:

所以我认为解决方案是这样的

json_string = json.dumps(response.to_dict())
# which you can then input into a df

基本上,我们从从 API 返回字典转移到返回 Python 模型。所以我们需要从model -> dictionary -> json 出发。 to_dict 是每个模型上输出字典的方法,然后json.dumps 接收字典并将其转换为有效的 JSON。

LMK 如果这对你有用:)

【讨论】:

  • 谢谢!这足以让我继续前进:json_string = json.loads(json.dumps(response.to_dict())) df = pd.json_normalize(json_string, record_path=['accounts'])
  • 我还没有足够的代表将您的答案标记为有用,但感谢您的帮助!
猜你喜欢
  • 2022-06-19
  • 2021-01-19
  • 2021-07-13
  • 2021-10-18
  • 2016-09-19
  • 2019-10-03
  • 1970-01-01
  • 2021-09-26
  • 1970-01-01
相关资源
最近更新 更多