#python-3.x #pandas #list #dataframe
Вопрос:
У меня есть следующий df:
df6 = pd.DataFrame({'name':['Sara', 'John', 'Jack'],
'places': ['UK,UK,UK,UK,US,CA', 'US,US,US,CA,CA,CA', 'Mexico,AUS,AUS,Mexico,Mexico']
})
df6
Выглядит как:
name places
0 Sara UK,UK,UK,UK,US,CA
1 John US,US,US,CA,CA,CA
2 Jack Mexico,AUS,AUS,Mexico,Mexico
Колонка «Места» посвящена только 5 странам. То, что я пытаюсь сделать, — это найти количество последовательных визитов в каждую страну. Таким образом, в основном результат будет таким:
name UK US CA Mexico AUS
0 Sara 4 0 0 0 0
1 John 0 3 3 0 0
2 Jack 0 0 0 2 2
Шаги, которые я сделал до сих пор, это:
df6['consecutive'] = df6.places.map(lambda x: [Counter(group[1]) for group in groupby(x.split(','))])
Это дает мне list of dicts
:
name places consecutive
0 Sara UK,UK,UK,UK,US,CA [{'UK': 4}, {'US': 1}, {'CA': 1}]
1 John US,US,US,CA,CA,CA [{'US': 3}, {'CA': 3}]
2 Jack Mexico,AUS,AUS,Mexico,Mexico [{'Mexico': 1}, {'AUS': 2}, {'Mexico': 2}]
Теперь я остановился здесь на том, как перебирать каждую ячейку в последовательном столбце, чтобы найти values > 1
каждую ячейку и преобразовать df6 в конечный результат:
name UK US CA Mexico AUS
0 Sara 4 0 0 0 0
1 John 0 3 3 0 0
2 Jack 0 0 0 2 2
Комментарии:
1. Вы просто берете максимальное последовательное значение или последнее? У Джека есть Мексика 1 и Мексика 2.
2. Значения > 1, потому что в моих данных, если значение равно 1, это означает только одно посещение, поэтому для Джека я выбираю Мексику 2 и Австралию 2
3. Да, но если бы у тебя был Джек
Mexico, Mexico, Mexico, AUS, AUS, Mexico, Mexico
, что бы ты оставил себе ?
Ответ №1:
Вы можете использовать pd.crosstab
:
df6["places"] = df6["places"].apply(lambda x: x.split(","))
df6 = df6.explode("places")
out = pd.crosstab(df6["name"], df6["places"])
out.index.name = None
out.columns.name = None
print(out)
С принтами:
AUS CA Mexico UK US
Jack 2 0 3 0 0
John 0 3 0 0 3
Sara 0 1 0 4 1
ИЗМЕНИТЬ: Для consecutive
столбца суммы (для последовательных значений > 1>):
from itertools import groupby
from collections import Counter
df6["consecutive"] = df6.places.map(
lambda x: [
{k: v for k, v in Counter(group[1]).items() if v > 1}
for group in groupby(x.split(","))
]
)
df6 = df6.explode("consecutive").reset_index(drop=True)
out = (
pd.concat([df6, pd.DataFrame(df6.pop("consecutive").tolist())], axis=1)
.groupby("name")
.sum()
)
print(out)
С принтами:
UK US CA AUS Mexico
name
Jack 0.0 0.0 0.0 2.0 2.0
John 0.0 3.0 3.0 0.0 0.0
Sara 4.0 0.0 0.0 0.0 0.0
Комментарии:
1. Спасибо, Андрей, ваши выходные данные показывают все визиты в каждую страну. Я ищу способ найти только
consecutive visits
это, поэтому я использовалdf6['consecutive'] = df6.places.map(lambda x: [Counter(group[1]) for group in groupby(x.split(','))])
для поиска последовательных значений на основе отсортированного списка, разделенного запятыми, в столбцеplaces
Ответ №2:
Мы можем str.split
и explode
places
. Затем воспользуйтесь groupby size
фильтром и, unstack
чтобы получить количество последовательных loc
посещений, включающим только посещения, превышающие 1 последовательное. Затем groupby sum
сократить до одной строки на имя и join
вернуться к исходному кадру данных:
places = df6["places"].str.split(',').explode() # Each place in own row
df7 = df6[['name']].join(
places.groupby(
[df6['name'], # Name
places, # Places
# consecutive duplicates in separate groups
places.ne(places.shift()).groupby(df6['name']).cumsum()]
).size() # Count how many in each group
.loc[lambda x: x > 1] # Filter to include only > 1 visits
.unstack(1, fill_value=0) # Make places columns
.groupby(level=0).sum(), # Get single row per name
on='name' # join back on name column
)
df7
:
name AUS CA Mexico UK US
0 Sara 0 0 0 4 0
1 John 0 3 0 0 3
2 Jack 2 0 2 0 0
Ответ №3:
Или вы можете использовать сводную таблицу:
import pandas as pd
df6 = pd.DataFrame({'name':['Sara', 'John', 'Jack'],
'places': ['UK,UK,UK,UK,US,CA', 'US,US,US,CA,CA,CA', 'Mexico,AUS,AUS,Mexico,Mexico']
})
df6['places'] = df6.places.str.split(',')
df6 = df6.explode('places')
df6['lag_places'] = df6.places.shift(1)
df6 = df6.query('places == lag_places').pivot_table(index = 'name', columns = 'places', aggfunc = 'count')
df6.loc[:, df6.columns != 'places'] = df6.loc[:, df6.columns != 'places'].apply(lambda x: x 1) # add 1 according to your definition
df6.columns = [x[1] for x in df6.columns]
df6.fillna(0, inplace = True)
# AUS CA Mexico UK US
#name
#Jack 2.0 0.0 2.0 0.0 0.0
#John 0.0 3.0 0.0 0.0 3.0
#Sara 0.0 0.0 0.0 4.0 0.0