#pandas
#pandas
Вопрос:
У меня есть этот набор данных транзакций:
------ -------- ------- -------
| name | amount | feat1 | feat2 |
------ -------- ------- -------
| dave | 24 | - | - |
------ -------- ------- -------
| john | 55 | - | - |
------ -------- ------- -------
| dave | 56 | - | - |
------ -------- ------- -------
| dave | 94 | - | - |
------ -------- ------- -------
| dave | 10 | - | - |
------ -------- ------- -------
| john | 3 | - | - |
------ -------- ------- -------
| dave | 7 | - | - |
------ -------- ------- -------
| john | 77 | - | - |
------ -------- ------- -------
| john | 88 | - | - |
------ -------- ------- -------
| dave | 34 | - | - |
------ -------- ------- -------
| john | 84 | - | - |
------ -------- ------- -------
| john | 22 | - | - |
------ -------- ------- -------
Я хочу создать последовательности, которые будут выглядеть так:
window_size
прошлые транзакции -> следующая транзакция, но для каждого клиента. Это означает, что window_size = 4
я хочу, чтобы результат выглядел примерно так:
source:
------ -------- ------- -------
| name | amount | feat1 | feat2 |
------ -------- ------- -------
| dave | 24 | - | - | next:
------ -------- ------- -------
| dave | 56 | - | - | ------ -------- ------- -------
------ -------- ------- ------- | dave | 7 | - | - |
| dave | 94 | - | - | ------ -------- ------- -------
------ -------- ------- -------
| dave | 10 | - | - |
------ -------- ------- -------
source:
------ -------- ------- -------
| name | amount | feat1 | feat2 |
------ -------- ------- ------- next:
| dave | 56 | - | - |
------ -------- ------- ------- ------ -------- ------- -------
| dave | 94 | - | - | | dave | 34 | - | - |
------ -------- ------- ------- ------ -------- ------- -------
| dave | 10 | - | - |
------ -------- ------- -------
| dave | 7 | - | - |
------ -------- ------- -------
source:
------ -------- ------- -------
| name | amount | feat1 | feat2 |
------ -------- ------- ------- next:
| john | 55 | - | - |
------ -------- ------- ------- ------ -------- ------- -------
| john | 3 | - | - | | john | 84 | - | - |
------ -------- ------- ------- ------ -------- ------- -------
| john | 77 | - | - |
------ -------- ------- -------
| john | 88 | - | - |
------ -------- ------- -------
source:
------ -------- ------- -------
| name | amount | feat1 | feat2 |
------ -------- ------- ------- next:
| john | 3 | - | - |
------ -------- ------- ------- ------ -------- ------- -------
| john | 77 | - | - | | john | 22 | - | - |
------ -------- ------- ------- ------ -------- ------- -------
| john | 88 | - | - |
------ -------- ------- -------
| john | 84 | - | - |
------ -------- ------- -------
и то же самое для другого пользователя ‘john’ и вообще для каждого пользователя.
Я хочу, чтобы мои выходные данные представляли собой две переменные python: sequences
and targets
sequences
будут иметь форму (window_size, num_features)
и tragets
будут иметь форму (1, num_features)
Комментарии:
1. Какую операцию вы хотели бы выполнить за прошлые транзакции? Результатом должно быть
mean
значение,min
или что-то другое?2. просто сложите их вместе и сохраните как фрейм данных
3. Но как вы получаете
target
данные из прошлых транзакций?4. просто в следующей транзакции я отредактировал свой вопрос, чтобы спроецировать это
Ответ №1:
Учитывая, что ваш фрейм данных вызывается df
, вы можете попробовать следующий код:
window_size = 4
my_transactions = {}
for name in df['name'].unique():
_user_transactions = df[df['name'] == name]
my_transactions[name] = [
{
'past': _user_transactions.iloc[i:i window_size],
'target': _user_transactions.iloc[i window_size]
}
for i in range(len(_user_transactions) - window_size)
]
Это вернет a dictionary
с ключами, равными именам пользователей.
Для каждого пользователя будет список словарей, каждый из которых содержит ключи past
и target
, содержащий соответствующие фреймы данных. Смотрите пример ниже
for name in my_transactions:
print('User:', name)
for transaction in my_transactions[name]:
print('Past transactions:')
print(transaction['past'])
print('Target transaction:')
print(transaction['target'])
print()
print()
Для пользователя dave
вывод:
User: dave
Past transactions:
name amount
0 dave 24
2 dave 56
3 dave 94
4 dave 10
Target transaction:
name dave
amount 7
Name: 6, dtype: object
Past transactions:
name amount
2 dave 56
3 dave 94
4 dave 10
6 dave 7
Target transaction:
name dave
amount 34
Name: 9, dtype: object
И для john
User: john
Past transactions:
name amount
1 john 55
5 john 3
7 john 77
8 john 88
Target transaction:
name john
amount 84
Name: 10, dtype: object
Past transactions:
name amount
5 john 3
7 john 77
8 john 88
10 john 84
Target transaction:
name john
amount 22
Name: 11, dtype: object
Сообщите нам, если у вас возникнут какие-либо дополнительные проблемы.
Комментарии:
1. Спасибо за ваше решение, оно действительно работает. Мне было интересно, масштабируется ли он для наборов данных с миллионами записей. Как вы думаете, это можно реализовать более эффективным способом? например, используя pandas’
groupby
и ?rolling
Ответ №2:
import pandas as pd
from tabulate import tabulate
def extract_window_and_target(df, i, window_size):
# get the data with same user and with index bigger than i
user_name = df.iloc[i, :][0]
user_events_from_i = df.iloc[i:, :].loc[df["name"] == user_name, :]
# extract window and target
X = user_events_from_i.iloc[:window_size, :].reset_index(drop=True)
y = user_events_from_i.iloc[window_size:window_size 1, :].reset_index(drop=True)
has_empty_target = True if len(y) == 0 else False
return X, y, has_empty_target
columns = ["name", "amount"]
data = [["dave", "24"],
["john", "55"],
["dave", "56"],
["dave", "94"],
["dave", "10"],
["john", "3"],
["dave", "7"],
["john", "77"],
["john", "88"],
["dave", "34"],
["john", "84"],
["john", "22"]]
df = pd.DataFrame(data, columns=columns)
window_size = 4
for i in range(len(df)):
X, y, has_empty_target = extract_window_and_target(df, i, window_size)
if not has_empty_target:
print("window (training data) :")
print(tabulate(X, headers=X.columns, tablefmt="psql"))
print("target:")
print(tabulate(y, headers=y.columns, tablefmt="psql"))
Вывод:
window 0 (training data) :
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | dave | 24 |
| 1 | dave | 56 |
| 2 | dave | 94 |
| 3 | dave | 10 |
---- -------- ----------
target 0:
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | dave | 7 |
---- -------- ----------
window 1 (training data) :
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | john | 55 |
| 1 | john | 3 |
| 2 | john | 77 |
| 3 | john | 88 |
---- -------- ----------
target 1:
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | john | 84 |
---- -------- ----------
window 2 (training data) :
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | dave | 56 |
| 1 | dave | 94 |
| 2 | dave | 10 |
| 3 | dave | 7 |
---- -------- ----------
target 2:
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | dave | 34 |
---- -------- ----------
window 5 (training data) :
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | john | 3 |
| 1 | john | 77 |
| 2 | john | 88 |
| 3 | john | 84 |
---- -------- ----------
target 5:
---- -------- ----------
| | name | amount |
|---- -------- ----------|
| 0 | john | 22 |
---- -------- ----------
Это также будет работать, если у вас есть несколько функций в Dataframe.
(.. однако это не оптимальное решение для скорости)
Комментарии:
1. Спасибо за ваше решение. После того, как я был осужден, это действительно работает, мне интересно, какое это лучшее решение с точки зрения скорости. Может быть, используя pandas
groupby
иrolling
функции?2. да, прокатка и применение
extract_window_and_target
повысят скорость, потому что вы можете избежать фильтрации всего набора данных на каждой итерации. однако, если вы принимаете факт потери хронологического порядка транзакции, решение Ralobrusto должно быть быстрее (он фильтрует только один раз для каждого имени).