панды получают уникальные значения из столбца списков - PullRequest
2 голосов
/ 23 октября 2019

Как получить уникальные значения столбца списков в пандах или numpy, чтобы второй столбец

enter image description here

мог привести к «действию», 'криминальная драма'. Наиболее близкими (но не функциональными) решениями, которые я мог придумать, были:

 genres = data['Genre'].unique()

Но это, как и следовало ожидать, приводит к тому, что TypeError сообщает, что списки не могут быть хешируемыми.

TypeError: unhashable type: 'list'

Набор, казалось бы, хорошая идея, но

genres = data.apply(set(), columns=['Genre'], axis=1)

, но также приводит к TypeError: set() takes no keyword arguments

Ответы [ 5 ]

2 голосов
/ 23 октября 2019

Вы можете использовать explode:

data = pd.DataFrame([
    {
        "title": "The Godfather: Part II",
        "genres": ["crime", "drama"],
        "director": "Fracis Ford Coppola"
    },
    {
        "title": "The Dark Knight",
        "genres": ["action", "crime", "drama"],
        "director": "Christopher Nolan"
    }
])
# Changed from data.explode("genres")["genres"].unique() as suggested by rafaelc
data["genres"].explode().unique() 

Результаты:

array(['crime', 'drama', 'action'], dtype=object)
2 голосов
/ 23 октября 2019

Если вы хотите найти только уникальные значения, я бы рекомендовал использовать itertools.chain.from_iterable для объединения всех этих списков

import itertools

>>> np.unique([*itertools.chain.from_iterable(df.Genre)])
array(['action', 'crime', 'drama'], dtype='<U6')

Или даже быстрее

>>> set(itertools.chain.from_iterable(df.Genre))
{'action', 'crime', 'drama'}

Timings

df = pd.DataFrame({'Genre':[['crime','drama'],['action','crime','drama']]})
df = pd.concat([df]*10000)

%timeit set(itertools.chain.from_iterable(df.Genre))
100 loops, best of 3: 2.55 ms per loo

%timeit set([x for y in df['Genre'] for x in y])
100 loops, best of 3: 4.09 ms per loop

%timeit np.unique([*itertools.chain.from_iterable(df.Genre)])
100 loops, best of 3: 12.8 ms per loop

%timeit np.unique(df['Genre'].sum())
1 loop, best of 3: 1.65 s per loop

%timeit set(df['Genre'].sum())
1 loop, best of 3: 1.66 s per loop
1 голос
/ 23 октября 2019

Вот несколько вариантов:

# toy data
df = pd.DataFrame({'Genre':[['crime','drama'],['action','crime','drama']]})

np.unique(df['Genre'].sum())
# 109 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

set(df['Genre'].sum())
# 87 µs ± 1.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

set([x  for y in df['Genre'] for x in y])
# 11.8 µs ± 126 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
0 голосов
/ 23 октября 2019

Не уверен, что это именно то, что вы хотели, но это позволит вам преобразовать его в набор.

import pandas as pd
import numpy as np

df = pd.DataFrame({'Movie':['The Godfather', 'Dark Knight'], 'Genre': [['Crime', 'Drama'],['Crime', 'Drama', 'Action']]})

genres = []
for sublist in df['Genre']:
    for item in sublist:
        genres.append(item)

genre_set = set(genres)

print(genre_set)

Вывод: {'Action', 'Drama', 'Crime'}

0 голосов
/ 23 октября 2019

Если вы просто хотите извлечь информацию, а не добавить обратно в DataFrame, вы можете использовать метод set Python в цикле for:

import pandas as pd
df = pd.DataFrame({'movie':[[1,2,3],[1,2,6]]})
out = set()
for row in df['movie']:
    out.update({item for item in row})
print(out)

Вы также можете заключить это в вызове applyесли вы хотите (который вернет None, но обновит набор на месте):

out = set()
df['movie'].apply(lambda x: out.update({item for item in x}))

Лично я думаю, что цикл for немного понятнее для чтения.

...