Резюме
Короче говоря, мне нужно извлечь данные из серии панд, содержащих отдельные OrderedDicts.Пока прогресс был хорошим, но теперь я наткнулся на камень преткновения.
Когда я определяю свой собственный кадр данных для демонстрационных целей при переполнении стека, я могу использовать функцию индексации OrderedDict для поиска данных, которые я 'м после в пределах OrderedDict.Тем не менее, когда я работаю с реальными данными, где я не определяю OrderedDict внутри фрейма данных, мне приходится анализировать OrderedDict через стандартный пакет Json с помощью функции.
У OrderedDicts, с которыми я работаю, несколько вложенныхиерархии, которые могут манипулировать обычным способом ...
from collections import OrderedDict
example = OrderedDict([('attributes', OrderedDict([('type', 'Name'), ('url', 'URLHERE')])), ('UserRole', OrderedDict([('attributes', OrderedDict([('type', 'UserRole'), ('url', 'URLHERE')])), ('Name', 'Telephone Sales')]))])
print(example['UserRole']['Name'])
Приведенный выше код приведет к 'Telephone Sales'
.Однако это работает только тогда, когда я определил DataFrame вручную для примера, так как мне нужно использовать пакет collection.OrderedDict без необходимости разбора.
Фон
Ниже приведен некоторый код, который я подготовил для StackOverflow и который демонстрирует мою проблему.
import pandas as pd
import json
from collections import OrderedDict
# Settings
pd.set_option('display.max_colwidth', -1)
# Functions
def extract_odict_item(odict, key_1, key_2=None):
data = json.dumps(odict)
final_data = json.loads(data)
if key_2 is None:
if final_data is not None:
return final_data[key_1]
else:
return None
elif key_2 is not None:
if final_data is not None:
return final_data[key_1][key_2]
else:
return None
# Data
accounts = [
OrderedDict([('attributes', OrderedDict([('type', 'Account'), ('url', 'URLHERE')])), ('Name', 'Supermarket'), ('AccountNumber', 'ACC1234'), ('MID__c', '123456789')]),
OrderedDict([('attributes', OrderedDict([('type', 'Account'), ('url', 'URLHERE')])), ('Name', 'Bar'), ('AccountNumber', 'ACC9876'), ('MID__c', '987654321')]),
OrderedDict([('attributes', OrderedDict([('type', 'Account'), ('url', 'URLHERE')])), ('Name', 'Florist'), ('AccountNumber', 'ACC1298'), ('MID__c', '123459876')])
]
owner = [
OrderedDict([('attributes', OrderedDict([('type', 'Name'), ('url', 'URLHERE')])), ('UserRole', OrderedDict([('attributes', OrderedDict([('type', 'UserRole'), ('url', 'URLHERE')])), ('Name', 'Telephoone Sales')]))]),
OrderedDict([('attributes', OrderedDict([('type', 'Name'), ('url', 'URLHERE')])), ('UserRole', OrderedDict([('attributes', OrderedDict([('type', 'UserRole'), ('url', 'URLHERE')])), ('Name', 'Field Sales')]))]),
OrderedDict([('attributes', OrderedDict([('type', 'Name'), ('url', 'URLHERE')])), ('UserRole', OrderedDict([('attributes', OrderedDict([('type', 'UserRole'), ('url', 'URLHERE')])), ('Name', 'Online Sale')]))])
]
# Dataframe
df = pd.DataFrame({'ConvertedAccounts': accounts,
'Owner': owner
})
# Extract data from OrderedDict using usual indexing
df['MerchantID'] = df['ConvertedAccounts'].apply(lambda x: x['MID__c'])
df['UserRole'] = df['Owner'].apply(lambda x: x['UserRole']['Name'])
# Extract data from OrderedDict using function
df['extracted_MerchantID'] = df['ConvertedAccounts'].apply(lambda x: extract_odict_item(x, 'MID__c'))
df['extracted_UserRole'] = df['Owner'].apply(
lambda x: extract_odict_item(x, 'UserRole', 'Name'))
# Drop junk columns
df = df.drop(columns=['ConvertedAccounts', 'Owner'])
print(df)
В приведенном выше коде у меня есть функция extract_odict_item (), которую я могу использовать для извлечения данных из каждого отдельного OrderedDict в рамках фрейма данных и помещенияэто в новый столбец, пока я указываю, что я хочу.Однако я хочу иметь возможность указать столько аргументов, сколько я хочу, с помощью * args, чтобы указать, сколько гнезд я хочу обойти и извлечь значение из окончательного ключа.
Ожидаемые результаты
Я хочу иметь возможность использовать приведенную ниже функцию для приема нескольких аргументов и создания вложенного селектора индекса, например, так ...
# Functions
def extract_odict_item(odict, *args):
data = json.dumps(odict)
final_data = json.loads(data)
if len(args) == 0:
raise Exception('Requires atleast 1 argument')
elif len(args) == 1:
if final_data is not None:
return final_data[args[0]]
else:
return None
elif len(args) > 1:
### Pseudo Code ###
# if final_data is not None:
# return final_data[args[0]][args[1]][args[2]] etc.....
# else:
# return None
Так что если я вызываю extract_odict_item
extract_odict_item(odict, 'item1', 'item2', 'item3')
Он должен вернуть final_data['item1']['item2']['item3']
Возможно, я слишком усложнил это, но я не могу думать ни о чем другом или даже если это возможно в Python.
Ответ
Мне удалось использовать рекурсивную функцию для обработки выбора нужных мне данных из заказанного приговора
import json
from collections import OrderedDict
# Settings
pd.set_option('display.max_colwidth', -10)
# Data
owner = [
OrderedDict([('attributes', OrderedDict([('type', 'Name'), ('url', 'URLHERE')])), ('UserRole', OrderedDict([('attributes', OrderedDict([('type', 'UserRole'), ('url', 'URLHERE')])), ('Name', 'Telephoone Sales')]))]),
OrderedDict([('attributes', OrderedDict([('type', 'Name'), ('url', 'URLHERE')])), ('UserRole', OrderedDict([('attributes', OrderedDict([('type', 'UserRole'), ('url', 'URLHERE')])), ('Name', 'Field Sales')]))]),
OrderedDict([('attributes', OrderedDict([('type', 'Name'), ('url', 'URLHERE')])), ('UserRole', OrderedDict([('attributes', OrderedDict([('type', 'UserRole'), ('url', 'URLHERE')])), ('Name', 'Online Sale')]))])
]
# Functions
def rec_ext(odict, item_list):
new_list = item_list.copy()
data = json.dumps(odict)
final_data = json.loads(data)
el = new_list.pop()
if isinstance(final_data[el], dict):
return rec_ext(final_data[el], new_list)
else:
return final_data[el]
# Dataframe
df = pd.DataFrame({'owner': owner
})
my_columns = ['UserRole', 'Name']
my_columns.reverse()
df['owner2'] = df['owner'].apply(lambda x: rec_ext(x, my_columns))
print(df['owner2'])