1 Câu hỏi: Cách trích xuất dữ liệu từ hệ thống phân cấp được sắp xếp theo thứ tự bằng cách sử dụng Python & Pandas

câu hỏi được tạo ra tại Wed, May 8, 2019 12:00 AM

Tóm tắt

Tóm lại, tôi cần trích xuất dữ liệu từ một loạt gấu trúc có chứa OrderedDicts riêng lẻ. Cho đến nay tiến độ vẫn tốt nhưng hiện tại tôi đã gặp phải trở ngại.

Khi tôi xác định khung dữ liệu của riêng mình cho mục đích trình diễn trên Stack Overflow, tôi có thể sử dụng chức năng lập chỉ mục OrderedDict để tìm dữ liệu mà tôi theo sau trong OrderedDict. Tuy nhiên, khi tôi làm việc với dữ liệu thực mà tôi không xác định OrderedDict trong khung dữ liệu, tôi phải phân tích OrderedDict thông qua gói Json tiêu chuẩn bằng cách sử dụng hàm.

Các OrderedDicts tôi đang làm việc có nhiều cấu trúc phân cấp lồng nhau có thể thao túng theo cách thông thường của ...

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'])

Đoạn mã trên sẽ dẫn đến 'Telephone Sales'. Tuy nhiên, điều này chỉ hoạt động khi tôi đã xác định DataFrame theo cách thủ công cho ví dụ khi tôi phải sử dụng các bộ sưu tập.OrderedDict mà không cần phân tích cú pháp.

Nền

Dưới đây là một số mã tôi đã chuẩn bị cho StackOverflow thể hiện một cách lỏng lẻo vấn đề của tôi.

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)

Trong đoạn mã trên, tôi có hàm extract_odict_item () mà tôi có thể sử dụng để trích xuất dữ liệu từ mỗi OrderedDict riêng lẻ trong khung dữ liệu và đặt nó vào một cột mới miễn là tôi chỉ định những gì tôi muốn. Tuy nhiên, tôi muốn có thể chỉ định bao nhiêu đối số mà tôi muốn thông qua * args để biểu thị có bao nhiêu tổ tôi muốn đi qua và trích xuất giá trị từ khóa cuối cùng.

Kết quả mong đợi

Tôi muốn có thể sử dụng hàm bên dưới để chấp nhận nhiều đối số và tạo bộ chọn chỉ mục lồng nhau như vậy ...

# 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

Vì vậy, nếu tôi gọi extract_odict_item

extract_odict_item(odict, 'item1', 'item2', 'item3')

Nó sẽ trả về final_data['item1']['item2']['item3']

Tôi có thể đã quá phức tạp việc này nhưng tôi không thể nghĩ gì khác hoặc nếu điều này thậm chí có thể xảy ra trong Python.

Trả lời

Tôi đã có thể sử dụng chức năng đệ quy để xử lý việc lựa chọn dữ liệu nào tôi cần từ lệnh được sắp xếp

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'])
    
1
1 Câu trả lời                              1                         

Đây không phải là một câu trả lời chính xác - nhưng bạn có thể thử đệ quy nếu tôi hiểu chính xác câu hỏi của bạn -

d = {1: {2: {3: {4: 5}}}}#Arbitrarily nested dict
l = [1, 2, 3, 4]

def rec_ext(my_dict, my_list):
     el = my_list.pop()
     if isinstance(my_dict[el], dict):
         return rec_ext(my_dict[el], my_list)
     else:
         return my_dict[el]

l.reverse() #we reverse because we are "popping" in the function 
rec_ext(d, l)
#Returns 5
    
1
2019-05-08 16: 39: 42Z
  1. Cảm ơn Mortz câu trả lời của bạn đã giúp rất nhiều! Vấn đề là tôi nhận được IndexError: pop từ danh sách trống khi tôi áp dụng chức năng cho cột Pandas bằng Lambda my_columns = ['UserRole', 'Name'] my_columns.reverse() df['owner2'] = df['owner'].apply(lambda x: rec_ext(x, my_columns))
    2019-05-09 09: 38: 20Z
  2. def rec_ext2(odict, column_list): new_list = column_list data = json.dumps(odict) final_data = json.loads(data) try: el = new_list.pop() if isinstance(final_data[el], dict): return rec_ext(final_data[el], new_list) else: return final_data[el] except IndexError: pass Thể hiện vấn đề cũng như chức năng hoạt động cho 1 mục trong chuỗi gấu trúc
    2019-05-09 09: 43: 00Z
  3. Lý do là danh sách my_columns được sửa đổi tại chỗ - có nghĩa là nó hoạt động cho hàng đầu tiên của khung dữ liệu của bạn và cho các hàng tiếp theo, danh sách my_columns. Bạn có thể giải quyết vấn đề này bằng cách gọi hàm của bạn là []. df['owner2'] = df['owner'].apply(lambda x: rec_ext(x, my_columns[:])) đảm bảo rằng bạn đang chuyển một bản sao của danh sách cho lệnh gọi hàm và không tham chiếu đến danh sách. Hy vọng điều này sẽ giúp
    2019-05-09 10: 45: 27Z
  4. Ồ wow tôi không biết điều này cảm ơn nhiều! Nó làm tôi tự hỏi nếu .reverse () & Các bước [:] có thể được tích hợp vào chính chức năng không?
    2019-05-09 10: 50: 46Z
  5. Có vẻ như bạn có thể đưa list.copy () vào hàm để tránh phải sử dụng danh sách [:] lát my_columns[:]
    2019-05-09 10: 55: 52Z
new_list = item_list.copy()
nguồn đặt đây