Bonjour, je suis ingénieur en apprentissage automatique. C'est Kawamoto. Il faisait trop froid et j'ai attrapé un rhume. Aujourd'hui, j'écrirai sur la façon d'effectuer le prétraitement tout en réduisant la consommation de mémoire pour les données de journal qui nécessitent la prise en compte des informations de série chronologique.
En supposant que vous disposez d'un ensemble de données avec de tels journaux de comportement pour chaque utilisateur
userid itemid categoryid timestamp
0 0 3 1 2019-01-04
1 0 4 1 2019-01-08
2 0 4 1 2019-01-19
3 0 5 1 2019-01-02
4 0 7 2 2019-01-17
5 0 8 2 2019-01-07
6 1 0 0 2019-01-06
7 1 1 0 2019-01-14
8 1 2 0 2019-01-20
9 1 6 2 2019-01-01
10 1 7 2 2019-01-12
11 1 8 2 2019-01-18
12 2 3 1 2019-01-16
13 2 4 1 2019-01-15
14 2 5 1 2019-01-10
15 2 5 1 2019-01-13
16 2 6 2 2019-01-03
17 2 7 2 2019-01-05
18 2 8 2 2019-01-11
19 2 8 2 2019-01-21
20 2 9 3 2019-01-09
Données de séries de longueur variable triées par temps pour chaque utilisateur comme indiqué ci-dessous,
Itemid (par ordre chronologique) que chaque utilisateur a contacté
[[5, 3, 8, 4, 7, 4],
[6, 0, 7, 1, 8, 2],
[6, 7, 9, 5, 8, 5, 4, 3, 8]]
Identifiant de la catégorie (par ordre chronologique) que chaque utilisateur a contacté
[[1, 1, 2, 1, 2, 1],
[2, 0, 2, 0, 2, 0],
[2, 2, 3, 1, 2, 1, 1, 1, 2]]
Je souhaite créer une variable catégorielle qui considère la série chronologique suivante. Ici, on suppose que le dernier enregistrement est utilisé pour itemid et categoryid.
Le dernier itemid que chaque utilisateur a contacté
[4, 2, 8]
Le dernier identifiant de catégorie que chaque utilisateur a contacté
[1, 0, 2]
Pour les données de série, si vous pouvez créer une telle liste, par exemple, Keras (API fonctionnelle) peut être entrée sous forme de données séquentielles par remplissage comme suit.
import tensorflow as tf
inputs = []
inputs.append(tf.keras.preprocessing.sequence.pad_sequences(
df['itemid'].values.tolist(), padding='post', truncating='post', maxlen=10))
inputs.append(tf.keras.preprocessing.sequence.pad_sequences(
df['categoryid'].values.tolist(), padding='post', truncating='post', maxlen=10))
En ce qui concerne les données de série, chez les pandas, si vous écrivez comme suit,
#Identifiant d'utilisateur,Trier par ordre chronologique
df = df.sort_values(by=['userid','timestamp'])
#Regrouper sous forme de liste pour chaque utilisateur
df = df.groupby('userid').agg(list).reset_index(drop=False)
print('Itemid (par ordre chronologique) que chaque utilisateur a contacté')
pprint.pprint(df['itemid'].values.tolist())
print('Identifiant de la catégorie (par ordre chronologique) que chaque utilisateur a contacté')
pprint.pprint(df['categoryid'].values.tolist()
Vous obtiendrez le résultat ci-dessus.
Itemid (par ordre chronologique) que chaque utilisateur a contacté
[[5, 3, 8, 4, 7, 4],
[6, 0, 7, 1, 8, 2],
[6, 7, 9, 5, 8, 5, 4, 3, 8]]
Identifiant de la catégorie (par ordre chronologique) que chaque utilisateur a contacté
[[1, 1, 2, 1, 2, 1],
[2, 0, 2, 0, 2, 0],
[2, 2, 3, 1, 2, 1, 1, 1, 2]]
Aussi, concernant les données de catégorie,
#Grouper par pour obtenir le dernier pour chaque utilisateur
df_cate = df.loc[df.groupby('userid')['timestamp'].idxmax()]
print(df_cate)
print('Le dernier itemid que chaque utilisateur a contacté')
pprint.pprint(df_cate['itemid'].values.tolist())
print('Le dernier identifiant de catégorie que chaque utilisateur a contacté')
pprint.pprint(df_cate['categoryid'].values.tolist())
Vous pouvez obtenir le résultat ci-dessus en écrivant comme.
Le dernier itemid que chaque utilisateur a contacté
[4, 2, 8]
Le dernier identifiant de catégorie que chaque utilisateur a contacté
[1, 0, 2]
Si l'ensemble de données ci-dessus devient volumineux, Pandas provoquera une erreur de mémoire et la conversion par lots ne sera pas possible. De plus, si le jeu de données lui-même ne tient pas dans la mémoire, il ne peut pas non plus le gérer. D'autre part, lors du traitement des ensembles de données séparément, il est nécessaire de conserver les informations de série chronologique des enregistrements qui ne sont pas inclus dans chaque ensemble de données.
De l'arrière-plan ci-dessus
Nous avions besoin d'une méthode pour créer des données de série dans un ordre chronologique comme celui-ci.
Ci-dessous, j'écrirai sur la méthode spécifique.
Ici, créez une liste contenant des informations sur les séries chronologiques, et en fonction de cela
J'écrirai sur la façon de créer.
Après cela, nous expliquerons quoi faire avec l'ensemble de données fractionné.
Tout d'abord, créez une liste à partir de laquelle vous pouvez effectuer des opérations dans l'ordre chronologique.
Si la valeur que vous souhaitez avoir en tant que données de série dans l'ordre chronologique et pour chaque utilisateur est ʻitem, et les informations de série chronologique de cette valeur sont
timestamp`,
[[[item,timestamp],[item,timestamp]...[item,timestamp]],
[[item,timestamp],[item,timestamp]...[item,timestamp]],
...
[[item,timestamp],[item,timestamp]...[item,timestamp]]]
Créez une liste tridimensionnelle appelée. Ici, la première dimension utilise l'ID utilisateur comme index.
Le processus est le suivant.
def create_list(df, user_index_col, sort_col, target_col, user_num):
"""
:param user_index_col:Colonne ID utilisateur
:param sort_col:Colonne contenant la valeur utilisée pour le tri
:param target_col:Colonne que vous souhaitez trier
:param user_num:Nombre d'utilisateurs (obtenu à partir de l'encodeur, etc.)
"""
inputs = [[] for _ in range(user_num)]
for _, user_index, sort_value, target_value in df[[user_index_col, sort_col, target_col]].itertuples():
inputs[user_index].append([target_value, sort_value])
return inputs
Si vous faites cela pour le premier ensemble de données qui sort,
itemid_inputs = create_list(df, user_index_col='userid', sort_col='timestamp', target_col='itemid', user_num=3)
categoryid_inputs = create_list(df, user_index_col='userid', sort_col='timestamp', target_col='categoryid', user_num=3)
print('itemid')
pprint.pprint(itemid_inputs)
print('categoryid')
pprint.pprint(categoryid_inputs)
Une liste comme celle ci-dessous sera créée.
itemid
[[[3, Timestamp('2019-01-04 00:00:00')],
[4, Timestamp('2019-01-08 00:00:00')],
[4, Timestamp('2019-01-19 00:00:00')],
[5, Timestamp('2019-01-02 00:00:00')],
[7, Timestamp('2019-01-17 00:00:00')],
[8, Timestamp('2019-01-07 00:00:00')]],
[[0, Timestamp('2019-01-06 00:00:00')],
[1, Timestamp('2019-01-14 00:00:00')],
[2, Timestamp('2019-01-20 00:00:00')],
[6, Timestamp('2019-01-01 00:00:00')],
[7, Timestamp('2019-01-12 00:00:00')],
[8, Timestamp('2019-01-18 00:00:00')]],
[[3, Timestamp('2019-01-16 00:00:00')],
[4, Timestamp('2019-01-15 00:00:00')],
[5, Timestamp('2019-01-10 00:00:00')],
[5, Timestamp('2019-01-13 00:00:00')],
[6, Timestamp('2019-01-03 00:00:00')],
[7, Timestamp('2019-01-05 00:00:00')],
[8, Timestamp('2019-01-11 00:00:00')],
[8, Timestamp('2019-01-21 00:00:00')],
[9, Timestamp('2019-01-09 00:00:00')]]]
categoryid
[[[1, Timestamp('2019-01-04 00:00:00')],
[1, Timestamp('2019-01-08 00:00:00')],
[1, Timestamp('2019-01-19 00:00:00')],
[1, Timestamp('2019-01-02 00:00:00')],
[2, Timestamp('2019-01-17 00:00:00')],
[2, Timestamp('2019-01-07 00:00:00')]],
[[0, Timestamp('2019-01-06 00:00:00')],
[0, Timestamp('2019-01-14 00:00:00')],
[0, Timestamp('2019-01-20 00:00:00')],
[2, Timestamp('2019-01-01 00:00:00')],
[2, Timestamp('2019-01-12 00:00:00')],
[2, Timestamp('2019-01-18 00:00:00')]],
[[1, Timestamp('2019-01-16 00:00:00')],
[1, Timestamp('2019-01-15 00:00:00')],
[1, Timestamp('2019-01-10 00:00:00')],
[1, Timestamp('2019-01-13 00:00:00')],
[2, Timestamp('2019-01-03 00:00:00')],
[2, Timestamp('2019-01-05 00:00:00')],
[2, Timestamp('2019-01-11 00:00:00')],
[2, Timestamp('2019-01-21 00:00:00')],
[3, Timestamp('2019-01-09 00:00:00')]]]
Ensuite, ajoutez le processus de tri de la liste créée dans l'ordre chronologique.
def sort_list(inputs, is_descending):
"""
:param is_descending:Que ce soit par ordre décroissant
"""
return [sorted(i_input, key=lambda i: i[1], reverse=is_descending) for i_input in inputs]
Lorsque ce processus est effectué,
itemid_inputs = sort_list(itemid_inputs, is_descending=False)
categoryid_inputs = sort_list(categoryid_inputs, is_descending=False)
print('itemid')
pprint.pprint(itemid_inputs)
print('categoryid')
pprint.pprint(categoryid_inputs)
Une liste triée par ordre chronologique est créée comme indiqué ci-dessous.
itemid
[[[5, Timestamp('2019-01-02 00:00:00')],
[3, Timestamp('2019-01-04 00:00:00')],
[8, Timestamp('2019-01-07 00:00:00')],
[4, Timestamp('2019-01-08 00:00:00')],
[7, Timestamp('2019-01-17 00:00:00')],
[4, Timestamp('2019-01-19 00:00:00')]],
[[6, Timestamp('2019-01-01 00:00:00')],
[0, Timestamp('2019-01-06 00:00:00')],
[7, Timestamp('2019-01-12 00:00:00')],
[1, Timestamp('2019-01-14 00:00:00')],
[8, Timestamp('2019-01-18 00:00:00')],
[2, Timestamp('2019-01-20 00:00:00')]],
[[6, Timestamp('2019-01-03 00:00:00')],
[7, Timestamp('2019-01-05 00:00:00')],
[9, Timestamp('2019-01-09 00:00:00')],
[5, Timestamp('2019-01-10 00:00:00')],
[8, Timestamp('2019-01-11 00:00:00')],
[5, Timestamp('2019-01-13 00:00:00')],
[4, Timestamp('2019-01-15 00:00:00')],
[3, Timestamp('2019-01-16 00:00:00')],
[8, Timestamp('2019-01-21 00:00:00')]]]
categoryid
[[[1, Timestamp('2019-01-02 00:00:00')],
[1, Timestamp('2019-01-04 00:00:00')],
[2, Timestamp('2019-01-07 00:00:00')],
[1, Timestamp('2019-01-08 00:00:00')],
[2, Timestamp('2019-01-17 00:00:00')],
[1, Timestamp('2019-01-19 00:00:00')]],
[[2, Timestamp('2019-01-01 00:00:00')],
[0, Timestamp('2019-01-06 00:00:00')],
[2, Timestamp('2019-01-12 00:00:00')],
[0, Timestamp('2019-01-14 00:00:00')],
[2, Timestamp('2019-01-18 00:00:00')],
[0, Timestamp('2019-01-20 00:00:00')]],
[[2, Timestamp('2019-01-03 00:00:00')],
[2, Timestamp('2019-01-05 00:00:00')],
[3, Timestamp('2019-01-09 00:00:00')],
[1, Timestamp('2019-01-10 00:00:00')],
[2, Timestamp('2019-01-11 00:00:00')],
[1, Timestamp('2019-01-13 00:00:00')],
[1, Timestamp('2019-01-15 00:00:00')],
[1, Timestamp('2019-01-16 00:00:00')],
[2, Timestamp('2019-01-21 00:00:00')]]]
Tout d'abord, le processus de création d'entités en série de longueur variable (entités séquentielles) à partir de la liste créée ci-dessus est le suivant.
def create_sequential(inputs):
#Supprimer la liste des horodatages dans la liste
return [[i[0] for i in i_input] for i_input in inputs]
Quand tu fais ça,
print('Itemid (par ordre chronologique) que chaque utilisateur a contacté')
pprint.pprint(create_sequential(itemid_inputs))
print('Identifiant de la catégorie (par ordre chronologique) que chaque utilisateur a contacté')
pprint.pprint(create_sequential(categoryid_inputs))
Vous pouvez obtenir le résultat recherché.
Itemid (par ordre chronologique) que chaque utilisateur a contacté
[[5, 3, 8, 4, 7, 4],
[6, 0, 7, 1, 8, 2],
[6, 7, 9, 5, 8, 5, 4, 3, 8]]
Identifiant de la catégorie (par ordre chronologique) que chaque utilisateur a contacté
[[1, 1, 2, 1, 2, 1],
[2, 0, 2, 0, 2, 0],
[2, 2, 3, 1, 2, 1, 1, 1, 2]]
Ensuite, le processus pour obtenir le dernier enregistrement de chaque utilisateur en tant que variable catégorielle à partir de la liste créée ci-dessus est le suivant.
def create_category(inputs, n=-1):
"""
:param n:Quel numéro conserver dans la liste chronologique
"""
#Supprimer la liste des horodatages dans la liste
#Ne laissez que les données de la nième série dans l'ordre chronologique
return [[i[0] for i in i_input][n] for i_input in inputs]
Quand tu fais ça,
print('Le dernier itemid que chaque utilisateur a contacté')
pprint.pprint(create_category(itemid_inputs, -1))
print('Le dernier identifiant de catégorie que chaque utilisateur a contacté')
pprint.pprint(create_category(categoryid_inputs, -1))
Vous pouvez obtenir le résultat recherché comme suit:
Le dernier itemid que chaque utilisateur a contacté
[4, 2, 8]
Le dernier identifiant de catégorie que chaque utilisateur a contacté
[1, 0, 2]
Ici, les fonctions séparées pour l'explication ci-dessus sont intégrées comme suit.
def create_features(
df, user_index_col, sort_col, target_col, user_num, is_descending, is_sequence, n=-1):
"""
:param user_index_col:Colonne ID utilisateur
:param sort_col:Colonne contenant la valeur utilisée pour le tri
:param target_col:Colonne que vous souhaitez trier
:param user_num:Nombre d'utilisateurs (obtenu à partir de l'encodeur, etc.)
:param is_descending:Que ce soit par ordre décroissant
:param is_sequence:Que ce soit séquentiel
:param n:Quel numéro conserver dans la liste chronologique (catégorie uniquement)
"""
#Créer une liste
inputs = [[] for _ in range(user_num)]
for _, user_index, sort_value, target_value in df[[user_index_col, sort_col, target_col]].itertuples():
inputs[user_index].append([target_value, sort_value])
#Trier la liste
inputs = [sorted(i_input, key=lambda i: i[1], reverse=is_descending) for i_input in inputs]
if is_sequence:
return [[i[0] for i in i_input] for i_input in inputs]
else:
return [[i[0] for i in i_input][n] for i_input in inputs]
C'est là que je voulais le plus écrire, si vous créez une liste pour contenir les informations de séries chronologiques comme décrit ci-dessus, si vous ne pouvez pas mettre toutes les données en mémoire, divisez l'ensemble de données et lisez-le avant chaque unité de division Il peut également être utilisé pour le traitement.
À titre d'exemple, supposons que le premier DataFrame soit divisé en trois parties et stocké dans le dictionnaire comme indiqué ci-dessous. (Je ne pense pas que ce soit réellement le cas, mais à titre d'exemple ...)
{'df1': userid itemid categoryid timestamp
0 0 3 1 2019-01-04
1 0 4 1 2019-01-08
2 0 4 1 2019-01-19
3 0 5 1 2019-01-02
4 0 7 2 2019-01-17
5 0 8 2 2019-01-07
6 1 0 0 2019-01-06,
'df2': userid itemid categoryid timestamp
7 1 1 0 2019-01-14
8 1 2 0 2019-01-20
9 1 6 2 2019-01-01
10 1 7 2 2019-01-12
11 1 8 2 2019-01-18
12 2 3 1 2019-01-16
13 2 4 1 2019-01-15,
'df3': userid itemid categoryid timestamp
14 2 5 1 2019-01-10
15 2 5 1 2019-01-13
16 2 6 2 2019-01-03
17 2 7 2 2019-01-05
18 2 8 2 2019-01-11
19 2 8 2 2019-01-21
20 2 9 3 2019-01-09}
Puisque les informations de série temporelle sont stockées dans la liste, elles peuvent être traitées en modifiant la fonction comme suit, par exemple.
def create_features_by_datasets(
df_dict, user_index_col, sort_col, target_col, user_num, is_descending, is_sequence, n=-1):
inputs = [[] for _ in range(user_num)]
#Traitement pour chaque unité de division de l'ensemble de données
for df in df_dict.values():
for _, user_index, sort_value, target_value in df[[user_index_col, sort_col, target_col]].itertuples():
inputs[user_index].append([target_value, sort_value])
inputs = [sorted(i_input, key=lambda i: i[1], reverse=is_descending) for i_input in inputs]
if is_sequence:
return [[i[0] for i in i_input] for i_input in inputs]
else:
return [[i[0] for i in i_input][n] for i_input in inputs]
Si vous procédez comme suit,
print('Itemid (par ordre chronologique) que chaque utilisateur a contacté')
pprint.pprint(create_features_by_datasets(df_dict, user_index_col='userid', sort_col='timestamp', target_col='itemid', user_num=3, is_descending=False, is_sequence=True))
print('Le dernier itemid que chaque utilisateur a contacté')
pprint.pprint(create_features_by_datasets(df_dict, user_index_col='userid', sort_col='timestamp', target_col='itemid', user_num=3, is_descending=False, is_sequence=False))
Le résultat est le même que ci-dessus.
Itemid (par ordre chronologique) que chaque utilisateur a contacté
[[5, 3, 8, 4, 7, 4],
[6, 0, 7, 1, 8, 2],
[6, 7, 9, 5, 8, 5, 4, 3, 8]]
Le dernier itemid que chaque utilisateur a contacté
[4, 2, 8]
De plus, cette fois, nous avons réduit les critères de tri aux informations de séries chronologiques, mais il est également possible de trier par d'autres colonnes ou par ordre décroissant. En changeant les variables passées pour le traitement ci-dessus, l'ordre croissant / décroissant et le tri en spécifiant des colonnes sont possibles.
Par exemple, dans l'ensemble de données suivant
userid itemid categoryid score
0 0 3 1 0.730968
1 0 3 1 0.889117
2 0 3 1 0.714828
3 0 4 1 0.430737
4 0 5 1 0.734746
5 0 7 2 0.412346
6 1 0 0 0.660430
7 1 3 1 0.095672
8 1 4 1 0.985072
9 1 5 1 0.629274
10 1 6 2 0.617733
11 1 7 2 0.636219
12 1 8 2 0.246769
13 1 8 2 0.020140
14 2 0 0 0.812525
15 2 1 0 0.671100
16 2 2 0 0.174011
17 2 2 0 0.164321
18 2 3 1 0.783329
19 2 4 1 0.068837
20 2 5 1 0.265281
Même s'il existe une colonne appelée score, si vous souhaitez créer des données de série et des données de catégorie dans l'ordre décroissant, le processus est le suivant.
print('Ordre des scores (itemid)')
pprint.pprint(create_features(df, user_index_col='userid', sort_col='score', target_col='itemid', user_num=3, is_descending=True, is_sequence=True))
print('Score maximum (itemid)')
pprint.pprint(create_features(df, user_index_col='userid', sort_col='score', target_col='itemid', user_num=3, is_descending=True, is_sequence=False, n=0))
Le résultat est le suivant.
Ordre des scores (itemid)
[[3, 5, 3, 3, 4, 7],
[4, 0, 7, 5, 6, 8, 3, 8],
[0, 3, 1, 5, 2, 2, 4]]
Score maximum (itemid)
[3, 4, 0]
Cette fois, j'ai écrit sur la façon de convertir les données de journal en fonctionnalités de série et en fonctionnalités de catégorie de manière à économiser la mémoire tout en tenant compte des informations de série chronologique. S'il existe un meilleur moyen, veuillez me le faire savoir dans les commentaires. Merci beaucoup.
Recommended Posts