如何从JSON列表列表创建padas.DataFrame

时间:2019-01-17 10:37:31

标签: python pandas dataframe

我有来自CSV(gist with small sample)的pandas DataFrame:

|  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 :(得分:0)

据我所知,没有办法以矢量化方式对Pandas数据帧执行JSON反序列化。您应该能够执行此操作的一种方法是使用.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

答案 1 :(得分:0)

如果您的csv数据如下所示。

(我将引号添加到流派json的键中只是为了轻松使用json包。由于这不是主要问题,您可以将其作为预处理来处理)

enter image description here

您将不得不遍历输入DataFrame的所有行。

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

在get_dataframe_for_a_row函数中:

  • 准备一个具有列标题和值行['title']的DataFrame
  • 添加具有通过将id附加到“ 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)

答案 2 :(得分:0)

没有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)