поиск вложенных столбцов в pandas кадре данных - PullRequest
7 голосов
/ 13 апреля 2020

У меня большой набор данных со многими столбцами в (сжатом) формате JSON. Я пытаюсь преобразовать его в паркет для последующей обработки. Некоторые столбцы имеют вложенную структуру. Сейчас я хочу проигнорировать эту структуру и просто записать эти столбцы в виде (JSON) строки.

Итак, для столбцов, которые я определил, я делаю:

df[column] = df[column].astype(str)

Однако я не уверен, какие столбцы являются вложенными, а какие нет. Когда я пишу с паркетом, я вижу это сообщение:

<stack trace redacted> 

  File "pyarrow/_parquet.pyx", line 1375, in pyarrow._parquet.ParquetWriter.write_table
  File "pyarrow/error.pxi", line 78, in pyarrow.lib.check_status
pyarrow.lib.ArrowInvalid: Nested column branch had multiple children: struct<coordinates: list<item: double>, type: string>

Это сигнал о том, что мне не удалось преобразовать один из моих столбцов из вложенного объекта в строку. Но какой столбец виноват? Как мне узнать?

Когда я печатаю .dtypes моего pandas фрейма данных, я не могу различить строковые и вложенные значения, потому что оба отображаются как object.

РЕДАКТИРОВАТЬ: ошибка дает подсказку относительно вложенного столбца, показывая детали структуры, но это отнимает много времени для отладки. Кроме того, он печатает только первую ошибку, и если у вас есть несколько вложенных столбцов, это может раздражать

Ответы [ 4 ]

2 голосов
/ 23 апреля 2020

Преобразование вложенной структуры в строку

Если я правильно понимаю ваш вопрос, вы хотите сериализовать эти вложенные Python объекты (list, dict) в пределах строк df до JSON и оставить другие элементы без изменений. , Лучше написать свой собственный метод приведения:

def json_serializer(obj):
    if isinstance(obj, [list, dict]): # please add other types that you considered as nested structure to the type list
        return json.dumps(obj)
    return obj

df = df.applymap(json_serializer)

В случае большого размера кадра данных astype(str) будет быстрее.

nested_cols = []
for c in df:
    if any(isinstance(obj, [list, dict]) for obj in df[c]):
        nested_cols.append(c)

for c in nested_cols:
    df[c] = df[c].astype(str) # this convert every element in the column independent of their types

Этот подход дает выигрыш в производительности благодаря для оценки короткого замыкания в вызове any(...). Он вернется сразу после попадания первого вложенного объекта в столбец и не будет тратить время на проверку остальных. Если какой-либо из методов «Dtype Introspection» подходит для ваших данных, использовать его будет еще быстрее.

Проверьте последнюю версию pyarrow

Я предполагаю, что эти вложенные структуры необходимо преобразовать в строку только потому, что они вызовут ошибку в pyarrow.parquet.write_table. Возможно, вам вообще не нужно конвертировать его, потому что проблема обработки вложенных столбцов в pyarrow, как сообщается, недавно была решена (29 марта 2020 г., версия 0.17.0). Но поддержка может быть проблемной c и при активное обсуждение .

1 голос
/ 29 апреля 2020

У меня была проблема с simialir при работе с Pyspark и потоковым набором данных, некоторые столбцы были вложенными, а некоторые - нет.

Учитывая, что ваш фрейм данных может выглядеть следующим образом:

df = pd.DataFrame({'A' : [{1 : [1,5], 2 : [15,25], 3 : ['A','B']}],
                   'B' : [[[15,25,61],[44,22,87],['A','B',44]]],
                   'C' : [((15,25,87),(22,91))],
                   'D' : 15,
                   'E' : 'A'
                  })


print(df)

                                         A  \
0  {1: [1, 5], 2: [15, 25], 3: ['A', 'B']}   

                                          B                         C   D  E  
0  [[15, 25, 61], [44, 22, 87], [A, B, 44]]  ((15, 25, 87), (22, 91))  15  A  

Мы можем сложить ваш фрейм данных и использовать apply с type, чтобы получить тип каждого столбца и передать его в словарь.

df.head(1).stack().apply(type).reset_index(0,drop=True).to_dict()
out:
{'A': dict, 'B': list, 'C': tuple, 'D': int, 'E': str}

, с этим мы можем использовать функцию для возврата кортежа Вложенные и не вложенные столбцы.


Функция

def find_types(dataframe):

    col_dict = dataframe.head(1).stack().apply(type).reset_index(0,drop=True).to_dict()
    unnested_columns = [k for (k,v) in col_dict.items() if v not in (dict,set,list,tuple)]
    nested_columns = list(set(col_dict.keys()) - set(unnested_columns))
    return nested_columns,unnested_columns

В действии.

nested,unested = find_types(df)

df[unested]

   D  E
0  15  A

print(df[nested])

                          C                                        A  \
0  ((15, 25, 87), (22, 91))  {1: [1, 5], 2: [15, 25], 3: ['A', 'B']}   

                                          B  
0  [[15, 25, 61], [44, 22, 87], [A, B, 44]]  
0 голосов
/ 27 апреля 2020

Если вы просто хотите выяснить, какие столбцы являются виновниками, просто напишите al oop, который записывает по одному столбцу за раз и хранит, какие из них терпят неудачу ...

bad_cols = []
for i in range(df.shape[1]):
    try:
        df.iloc[:, [i]].to_parquet(...)
    except KeyboardInterrupt:
        raise
    except Exception:  # you may want to catch ArrowInvalid exceptions instead
        bad_cols.append(i)
print(bad_cols)
0 голосов
/ 25 апреля 2020

Используя общую служебную функцию, такую ​​как infer_dtype() в pandas, вы можете определить, является ли столбец вложенным или нет.

from pandas.api.types import infer_dtype

for col in df.columns:
  if infer_dtype(df[col]) == 'mixed' : 
    # ‘mixed’ is the catchall for anything that is not otherwise specialized
    df[col] = df[col].astype('str')

Если вы нацеливаетесь на указанные c типы данных, затем см. Dtype Introspection

...