Как отсортировать панды данных на поле JSON - PullRequest
9 голосов
/ 09 апреля 2019

У меня есть такие данные в Pandas dataframe

   id     import_id              investor_id     loan_id      meta
   35736  unremit_loss_100312         Q05         0051765139  {u'total_paid': u'75', u'total_expense': u'75'}
   35737  unremit_loss_100313         Q06         0051765140  {u'total_paid': u'77', u'total_expense': u'78'}
   35739  unremit_loss_100314         Q06         0051765141  {u'total_paid': u'80', u'total_expense': u'65'}

Как сортировать на основе total_expense, которое является значением поля json
ex: total_expense для мета поля

Вывод долженбыть

id     import_id              investor_id     loan_id      meta
35739  unremit_loss_100314         Q06         0051765141  {u'total_paid': u'80', u'total_expense': u'65'}
35736  unremit_loss_100312         Q05         0051765139  {u'total_paid': u'75', u'total_expense': u'75'}
35737  unremit_loss_100313         Q06         0051765140  {u'total_paid': u'77', u'total_expense': u'78'}

Ответы [ 4 ]

3 голосов
/ 11 апреля 2019

Настройка и предварительная обработка

import ast
import numpy as np

if isinstance(x.at[0, 'meta'], str):
    df['meta'] = df['meta'].map(ast.literal_eval)

str.get с Series.argsort

df.iloc[df['meta'].str.get('total_expense').astype(int).argsort()]

      id            import_id investor_id   loan_id                                         meta
2  35739  unremit_loss_100314         Q06  51765141  {'total_paid': '80', 'total_expense': '65'}
0  35736  unremit_loss_100312         Q05  51765139  {'total_paid': '75', 'total_expense': '75'}
1  35737  unremit_loss_100313         Q06  51765140  {'total_paid': '77', 'total_expense': '78'}

Понимание списка

df.iloc[np.argsort([int(x.get('total_expense', '-1')) for x in df['meta']])]

      id            import_id investor_id   loan_id                                         meta
2  35739  unremit_loss_100314         Q06  51765141  {'total_paid': '80', 'total_expense': '65'}
0  35736  unremit_loss_100312         Q05  51765139  {'total_paid': '75', 'total_expense': '75'}
1  35737  unremit_loss_100313         Q06  51765140  {'total_paid': '77', 'total_expense': '78'}

Если вам нужно обработать NaNs / пропущенные данные, используйте

u = [  
  int(x.get('total_expense', '-1')) if isinstance(x, dict) else -1 
  for x in df['meta']
]
df.iloc[np.argsort(u)]

      id            import_id investor_id   loan_id                                         meta
2  35739  unremit_loss_100314         Q06  51765141  {'total_paid': '80', 'total_expense': '65'}
0  35736  unremit_loss_100312         Q05  51765139  {'total_paid': '75', 'total_expense': '75'}
1  35737  unremit_loss_100313         Q06  51765140  {'total_paid': '77', 'total_expense': '78'}
2 голосов
/ 09 апреля 2019

Использование:

print (df)
      id            import_id investor_id   loan_id  \
0  35736  unremit_loss_100312         Q05  51765139   
1  35736  unremit_loss_100312         Q05  51765139   
2  35736  unremit_loss_100312         Q05  51765139   

                                               meta  
0   {u'total_paid': u'75', u'total_expense': u'75'}  
1   {u'total_paid': u'75', u'total_expense': u'20'}  
2  {u'total_paid': u'75', u'total_expense': u'100'}  

import ast

df['meta'] = df['meta'].apply(ast.literal_eval)

df = df.iloc[df['meta'].str['total_expense'].astype(int).argsort()]

print (df)
      id            import_id investor_id   loan_id  \
1  35736  unremit_loss_100312         Q05  51765139   
0  35736  unremit_loss_100312         Q05  51765139   
2  35736  unremit_loss_100312         Q05  51765139   

                                           meta  
1   {'total_paid': '75', 'total_expense': '20'}  
0   {'total_paid': '75', 'total_expense': '75'}  
2  {'total_paid': '75', 'total_expense': '100'} 

Если возможно, если пропущен ключ total_expense для некоторой строки, преобразуйте пропущенные значения в некоторое целое число ниже, как и все остальные значения, например -1 для первой позиции этих строк:

print (df)
      id            import_id investor_id   loan_id  \
0  35736  unremit_loss_100312         Q05  51765139   
1  35736  unremit_loss_100312         Q05  51765139   
2  35736  unremit_loss_100312         Q05  51765139   

                                              meta  
0  {u'total_paid': u'75', u'total_expense': u'75'}  
1  {u'total_paid': u'75', u'total_expense': u'20'}  
2                           {u'total_paid': u'75'} 

df['meta'] = df['meta'].apply(ast.literal_eval)


df = df.iloc[df['meta'].str.get('total_expense').fillna(-1).astype(int).argsort()]
print (df)
      id            import_id investor_id   loan_id  \
2  35736  unremit_loss_100312         Q05  51765139   
1  35736  unremit_loss_100312         Q05  51765139   
0  35736  unremit_loss_100312         Q05  51765139   

                                          meta  
2                         {'total_paid': '75'}  
1  {'total_paid': '75', 'total_expense': '20'}  
0  {'total_paid': '75', 'total_expense': '75'}  

Другое решение:

df['new'] = df['meta'].str.get('total_expense').astype(int)
df = df.sort_values('new').drop('new', axis=1)
1 голос
/ 12 апреля 2019

РЕДАКТИРОВАТЬ 2:

Найден несколько лучший способ сделать это без использования apply:

from pandas.io.json import json_normalize

df = pd.concat([df, json_normalize(df['meta'])], axis = 1)\
       .sort_values(by = 'total_expense')\
       .drop(columns = ['total_paid', 'total_expense'])

РЕДАКТИРОВАТЬ:

df = pd.concat([df, df['meta'].apply(pd.Series)], axis = 1).sort_values(by = 'total_expense').drop(columns = ['total_paid', 'total_expense'])

Если вы хотите, чтобы он выглядел как оригинал, просто отбросьте столбцы, которые вы объединяете после сортировки.

Оригинал:

df = pd.concat([df, df['meta'].apply(pd.Series)], axis = 1).drop(columns ='meta').sort_values(by = 'total_expense')

df['meta'].apply(pd.Series) превращает диктанты в мета-столбце в свой собственный df.Мы можем объединить его с его оригиналом, удалить столбец мета (как избыточный), а затем отсортировать значения по «общим расходам»

0 голосов
/ 12 апреля 2019

Использование регулярного выражения:

df = pd.read_clipboard(r'\s\s+')
pattern = r"""u'total_expense': u'([0-9.]+)'"""
df['total_expense'] = df.meta.str.extract(pattern)
df.sort_values('total_expense')

Использование применить:

df['total_expense'] = df.meta.apply(eval).apply(
                        lambda x: x.get('total_expense', -1))
df.sort_values('total_expense')

Выход:

      id            import_id investor_id   loan_id  \
2  35739  unremit_loss_100314         Q06  51765141   
0  35736  unremit_loss_100312         Q05  51765139   
1  35737  unremit_loss_100313         Q06  51765140   

                                              meta total_expense  
2  {u'total_paid': u'80', u'total_expense': u'65'}            65  
0  {u'total_paid': u'75', u'total_expense': u'75'}            75  
1  {u'total_paid': u'77', u'total_expense': u'78'}            78  
...