Как создать padas.DataFrame из списка списка JSON - PullRequest
0 голосов
/ 17 января 2019

У меня есть pandas DataFrame из CSV ( gist с небольшим образцом ):

|  title   |                       genres               |
--------------------------------------------------------
| %title1% |[{id: 1, name: '...'}, {id: 2, name: '...'}]|
| %title2% |[{id: 2, name: '...'}, {id: 4, name: '...'}]|
...
| %title9% |[{id: 3, name: '...'}, {id: 9, name: '...'}]|

Каждый title может быть связан с различным количеством жанров (больше или больше 1).

Задача - преобразовать массивы из столбца genre в столбцы и поместить их (или True s) для каждого жанра:

|  title   | genre_1 | genre_2 | genre_3 | ... | genre_9 |
---------------------------------------------------------
| %title1% |    1    |    1    |    0    | ... |    0    |
| %title2% |    1    |    0    |    0    | ... |    0    |
...
| %title9% |    0    |    0    |    1    | ... |    1    |

Жанры - это константный набор (около 20 элементов в этом наборе).

Наивный метод:

  1. Создать набор всех жанров
  2. Создание столбцов для каждого жанра, заполненных 0
  3. Для каждой строки в DataFrame проверьте, есть ли некоторые жанры в столбцах genres, и заполните столбец для этого жанра 1.

Такой подход выглядит немного странно.

Я думаю, что у панд есть более подходящий метод для этого.

Ответы [ 3 ]

0 голосов
/ 17 января 2019

Если ваши CSV-данные выглядят так.

(я добавил кавычки к ключам жанров json просто для удобства работы с пакетом json. Поскольку это не главная проблема, вы можете сделать это в качестве предварительной обработки)

enter image description here

Вам придется перебирать все строки входного кадра данных.

for index, row in inputDf.iterrows():
    fullDataFrame = pd.concat([fullDataFrame, get_dataframe_for_a_row(row)])

в функции get_dataframe_for_a_row:

  • подготовить DataFrame с заголовком столбца и строкой значения ['title']
  • добавить столбцы с именами, образованными добавлением идентификатора к 'genre _'.
  • присвойте им значение 1

, а затем создайте DataFrame для каждой строки и объедините их в полный DataFrame. pd.concat () объединяет фрейм данных, полученный из каждой строки. объединит столбцы, если они уже существуют.

наконец, fullDataFrame.fillna(0) для замены NaN на 0

ваш окончательный DataFrame будет выглядеть следующим образом. enter image description here

вот полный код:

import pandas as pd
import json

inputDf = pd.read_csv('title_genre.csv')

def labels_for_genre(a):
    a[0]['id']
    labels = []
    for i in range(0 , len(a)):
        label = 'genre'+'_'+str(a[i]['id'])
        labels.append(label)
    return labels

def get_dataframe_for_a_row(row): 
    labels = labels_for_genre(json.loads(row['genres']))
    tempDf = pd.DataFrame()
    tempDf['title'] = [row['title']]
    for label in labels:
        tempDf[label] = ['1']
    return tempDf

fullDataFrame = pd.DataFrame()
for index, row in inputDf.iterrows():
    fullDataFrame = pd.concat([fullDataFrame, get_dataframe_for_a_row(row)])
fullDataFrame = fullDataFrame.fillna(0)
0 голосов
/ 19 января 2019

Полный рабочий раствор без iterrows:

import pandas as pd
import itertools
import json

# read data
movies_df = pd.read_csv('https://gist.githubusercontent.com/feeeper/9c7b1e8f8a4cc262f17675ef0f6e1124/raw/022c0d45c660970ca55e889cd763ce37a54cc73b/example.csv', converters={ 'genres': json.loads })

# get genres for all items
all_genres_entries = list(itertools.chain.from_iterable(movies_df['genres'].values))

# create the list with unique genres
genres = list({v['id']:v for v in all_genres_entries}.values())

# fill genres columns
for genre in genres:
    movies_df['genre_{}'.format(genre['id'])] = movies_df['genres'].apply(lambda x: 1 if genre in x else 0)
0 голосов
/ 17 января 2019

Насколько я знаю, не существует способа выполнить JSON-десериализацию на фрейме данных Pandas в векторизованном виде. Один из способов сделать это можно с помощью .iterrows(), который позволит вам сделать это за один цикл (хотя и медленнее, чем большинство встроенных операций с пандами).

import json

df = # ... your dataframe

for index, row in df.iterrows():
    # deserialize the JSON string
    json_data = json.loads(row['genres'])

    # add a new column for each of the genres (Pandas is okay with it being sparse)
    for genre in json_data:
        df.loc[index, genre['name']] = 1  # update the row in the df itself

df.drop(['genres'], axis=1, inplace=True)

Обратите внимание, что пустые ячейки должны быть заполнены NaN, а не 0 - вы должны использовать .fillna(), чтобы изменить это. Краткий пример со смутно похожим фреймом данных выглядит как

In [1]: import pandas as pd

In [2]: df = pd.DataFrame([{'title': 'hello', 'json': '{"foo": "bar"}'}, {'title': 'world', 'json': '{"foo": "bar", "ba
   ...: z": "boo"}'}])

In [3]: df.head()
Out[3]:
                           json  title
0                {"foo": "bar"}  hello
1  {"foo": "bar", "baz": "boo"}  world

In [4]: import json
   ...: for index, row in df.iterrows():
   ...:     data = json.loads(row['json'])
   ...:     for k, v in data.items():
   ...:         df.loc[index, k] = v
   ...: df.drop(['json'], axis=1, inplace=True)

In [5]: df.head()
Out[5]:
   title  foo  baz
0  hello  bar  NaN
1  world  bar  boo
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...