代码之家  ›  专栏  ›  技术社区  ›  feeeper

如何从json列表中创建padas.dataframe

  •  1
  • feeeper  · 技术社区  · 5 年前

    我有来自csv的pandas数据帧( gist with small sample )以下内容:

    |  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. 对于每一行,在数据框中检查某些类型是否在 genres 列并用1填充该流派的列。

    这种方法看起来有点奇怪。

    我认为熊猫有一个更合适的方法。

    3 回复  |  直到 5 年前
        1
  •  0
  •   Andrew F    5 年前

    据我所知,没有办法以矢量化的方式对pandas数据帧执行json反序列化。你应该能做到的一个方法是 .iterrows() 这将使您在一个循环中完成此操作(尽管比大多数内置pandas操作慢)。

    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
    
        2
  •  0
  •   darshanz    5 年前

    如果你的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函数中:

    • 准备一个列标题和值行为“title”的数据框
    • 添加通过将ID附加到“流派”而形成名称的列。
    • 给它们赋值为1

    然后为每一行构建一个数据框架并将它们连接到一个完整的数据框架。 concat()连接从每一行获得的数据帧。 将合并Comumns(如果已存在)。

    最后, fullDataFrame.fillna(0) 用0替换NaN

    您的最终数据帧将如下所示。 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)
    
        3
  •  0
  •   feeeper    5 年前

    完全工作解决方案 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)