Как преобразовать международную финансовую статистику JSON в фрейм данных pandas

#python #json #pandas #request #sdmx

#python #json #pandas #запрос #sdmx

Вопрос:

Я борюсь с данными Международного валютного фонда, которые представлены в формате JSON. После проверки некоторых сообщений я не мог понять, как это сделать.

Что я пробовал

 import requests
import pandas as pd
import json

# These are the variables I want to have as columns, plus setting a time index
var = ['NGDP_XDC', 'NCP_XDC', 'NCGG_XDC', 'NFI_XDC', 'NINV_XDC', 'NX_XDC', 
       'NM_XDC', 'NSDGDP_XDC', 'NGDP_R_K_IX', 'NGDP_D_IX']

# URL for the IMF JSON Restful Web Service,
# IFS database
base = 'http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/IFS/'
period = 'A'
country = 'MX'

var = 'NGDP_XDC NCP_XDC NCGG_XDC NFI_XDC NINV_XDC NX_XDC NM_XDC NSDGDP_XDC NGDP_R_K_IX NGDP_D_IX'
    
time = '?startPeriod=1970amp;endPeriod=2019'

# Get data from the above URL using the requests package
url = base   period   '.'   country   '.'   var   '.'   time

response = requests.get(url)
dictr = response.json()
  

… пока все хорошо… Тем не менее, это шаг, с которым я борюсь

 
flat = dictr['CompactData']['DataSet']['Series']

temp = pd.json_normalize(flat)
temp = temp.drop(columns=['@FREQ', '@REF_AREA', '@UNIT_MULT', '@BASE_YEAR'])
  

Я ожидал получить плоский файл, который я мог бы повернуть по своему желанию. Тем не менее, это то, что я получаю

 
    @INDICATOR @TIME_FORMAT                                                Obs
0     NINV_XDC          P1Y  [{'@TIME_PERIOD': '1970', '@OBS_VALUE': '37.21...
1       NX_XDC          P1Y  [{'@TIME_PERIOD': '1970', '@OBS_VALUE':

  

В который я понятия не имею, как его преобразовать

 year variable1 ... variableN

1970    10     ...    45
1980    20     ...    12
. 
.
.
2019    15     ...    10
  

Ответ №1:

Возможно, это подтолкнет вас в правильном направлении.

Значение ['CompactData']['DataSet']['Series'] — это a dict , которое содержит список dicts в качестве значения, которое вам нужно.

Итак, вам нужно сгладить это:

 series = response['CompactData']['DataSet']['Series']
flat = [item for sublist in [i['Obs'] for i in series] for item in sublist]
  

Собрать все это вместе:

 import requests
import pandas as pd

# These are the variables I want to have as columns, plus setting a time index
var = [
    'NGDP_XDC', 'NCP_XDC', 'NCGG_XDC', 'NFI_XDC', 'NINV_XDC', 'NX_XDC',
    'NM_XDC', 'NSDGDP_XDC', 'NGDP_R_K_IX', 'NGDP_D_IX',
]

base = 'http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/IFS/'
period = 'A'
country = 'MX'
time = '?startPeriod=1970amp;endPeriod=2019'

# Get data from the above URL using the requests package
url = f"{base}{period}.{country}.{' '.join(var)}.{time}"
response = requests.get(url).json()

series = response['CompactData']['DataSet']['Series']
flat = [item for sublist in [i['Obs'] for i in series] for item in sublist]
print(pd.DataFrame(flat))
  

Вывод:

     @TIME_PERIOD        @OBS_VALUE @OBS_STATUS
0           1970   37.210816346586         NaN
1           1971  35.6027864361386         NaN
2           1972   36.123021665698         NaN
3           1973  50.9603299629663         NaN
4           1974   80.992068185601         NaN
..           ...               ...         ...
[499 rows x 3 columns]
  

Ответ №2:

Я реализовал ваш nudge гораздо менее элегантным способом, поскольку не мог понять, как извлекать коды переменных и индекс времени из вашей процедуры. Это также работает:

 url = f"{base}{period}.{country}.{' '.join(var)}.{time}"
response = requests.get(url).json()
series = response['CompactData']['DataSet']['Series']

nipa = pd.DataFrame(index=range(1970, 2020))
N = len(var)

for n in range(0, N):
    temp = pd.DataFrame(series[n]['Obs'], index=range(1970, 2020))
    temp = temp.drop(columns='@TIME_PERIOD')
    temp.rename(columns={'@OBS_VALUE': var[n]}, inplace=True)
    nipa = pd.merge(nipa, temp, left_index=True, right_index=True)