[PYTHON] Speichersparende Konvertierung von Protokolldaten in die Merkmalsmenge der sequentiellen Kategorie unter Berücksichtigung von Zeitreihen

Einführung

Hallo, ich bin Ingenieur für maschinelles Lernen. Das ist Kawamoto. Es war zu kalt und ich erkältete mich. Heute werde ich darüber schreiben, wie eine Vorverarbeitung durchgeführt und gleichzeitig der Speicherverbrauch für Protokolldaten reduziert wird, bei denen Zeitreiheninformationen berücksichtigt werden müssen.

Was du machen willst

Angenommen, Sie haben für jeden Benutzer ein Dataset mit solchen Verhaltensprotokollen

    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

Seriendaten variabler Länge, sortiert nach Zeit für jeden Benutzer, wie unten gezeigt,

Itemid (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat
[[5, 3, 8, 4, 7, 4], 
 [6, 0, 7, 1, 8, 2], 
 [6, 7, 9, 5, 8, 5, 4, 3, 8]]
Kategorie-ID (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat
[[1, 1, 2, 1, 2, 1], 
 [2, 0, 2, 0, 2, 0], 
 [2, 2, 3, 1, 2, 1, 1, 1, 2]]

Ich möchte eine kategoriale Variable erstellen, die die folgenden Zeitreihen berücksichtigt. Hier wird angenommen, dass der neueste Datensatz für itemid und categoryid verwendet wird.

Die neueste Artikel-ID, die jeder Benutzer kontaktiert hat
[4, 2, 8]
Die neueste Kategorie-ID, die jeder Benutzer kontaktiert hat
[1, 0, 2]

Wenn Sie beispielsweise für Seriendaten eine solche Liste erstellen können, kann Keras (funktionale API) durch Auffüllen wie folgt als sequentielle Daten eingegeben werden.

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))

Wie man in Pandas schreibt

In Bezug auf Seriendaten in Pandas, wenn Sie wie folgt schreiben:

#Benutzeridentifikation,In chronologischer Reihenfolge sortieren
df = df.sort_values(by=['userid','timestamp'])
#Gruppieren nach als Liste für jeden Benutzer
df = df.groupby('userid').agg(list).reset_index(drop=False)

print('Itemid (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat')
pprint.pprint(df['itemid'].values.tolist())
print('Kategorie-ID (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat')
pprint.pprint(df['categoryid'].values.tolist()

Sie erhalten das obige Ergebnis.

Itemid (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat
[[5, 3, 8, 4, 7, 4], 
 [6, 0, 7, 1, 8, 2], 
 [6, 7, 9, 5, 8, 5, 4, 3, 8]]
Kategorie-ID (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat
[[1, 1, 2, 1, 2, 1], 
 [2, 0, 2, 0, 2, 0], 
 [2, 2, 3, 1, 2, 1, 1, 1, 2]]

Auch in Bezug auf Kategoriedaten,

#Gruppieren Sie nach, um für jeden Benutzer die neueste zu erhalten
df_cate = df.loc[df.groupby('userid')['timestamp'].idxmax()]

print(df_cate)
print('Die neueste Artikel-ID, die jeder Benutzer kontaktiert hat')
pprint.pprint(df_cate['itemid'].values.tolist())
print('Die neueste Kategorie-ID, die jeder Benutzer kontaktiert hat')
pprint.pprint(df_cate['categoryid'].values.tolist())

Sie können das obige Ergebnis erhalten, indem Sie wie schreiben.

Die neueste Artikel-ID, die jeder Benutzer kontaktiert hat
[4, 2, 8]
Die neueste Kategorie-ID, die jeder Benutzer kontaktiert hat
[1, 0, 2]

Mögliche Probleme mit Pandas

Wenn der obige Datensatz groß wird, verursacht Pandas einen Speicherfehler und eine Stapelkonvertierung ist nicht möglich. Wenn das Dataset selbst nicht in den Speicher passt, kann es auch nicht damit umgehen. Wenn Datensätze separat verarbeitet werden, müssen die Zeitreiheninformationen von Datensätzen beibehalten werden, die nicht in jedem Datensatz enthalten sind.

Was ich nochmal machen möchte

Aus dem obigen Hintergrund

Wir brauchten eine Methode, um Seriendaten in chronologischer Reihenfolge wie diese zu erstellen.

Im Folgenden werde ich über die spezifische Methode schreiben.

Methode

Erstellen Sie hier eine Liste mit Zeitreiheninformationen, die darauf basiert

--Series-Funktionen unter Berücksichtigung von Zeitreihen --Category Feature Quantity unter Berücksichtigung von Zeitreihen

Ich werde darüber schreiben, wie man erstellt.

Danach erklären wir, was mit dem geteilten Datensatz zu tun ist.

Erstellen einer Liste zum Sortieren

Erstellen Sie zunächst eine Liste, aus der Sie Vorgänge in chronologischer Reihenfolge ausführen können. Wenn der Wert, den Sie als Seriendaten in chronologischer Reihenfolge und für jeden Benutzer haben möchten, "Element" ist und die Zeitreiheninformationen dieses Werts "Zeitstempel" sind,

[[[item,timestamp],[item,timestamp]...[item,timestamp]],
 [[item,timestamp],[item,timestamp]...[item,timestamp]],
 ...
 [[item,timestamp],[item,timestamp]...[item,timestamp]]]

Erstellen Sie eine dreidimensionale Liste mit dem Namen. Hier verwendet die erste Dimension die Benutzer-ID als Index.

Der Prozess ist wie folgt.

def create_list(df, user_index_col, sort_col, target_col, user_num):
    """
    :param user_index_col:Benutzer-ID-Spalte
    :param sort_col:Spalte mit dem für die Sortierung verwendeten Wert
    :param target_col:Spalte, die Sie sortieren möchten
    :param user_num:Anzahl der Benutzer (vom Encoder usw. erhalten)
    """
    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

Wenn Sie dies für den ersten Datensatz tun, der herauskommt,

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)

Eine Liste wie die folgende wird erstellt.

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')]]]

In chronologischer Reihenfolge sortieren

Fügen Sie als Nächstes den Vorgang zum Sortieren der erstellten Liste in chronologischer Reihenfolge hinzu.

def sort_list(inputs, is_descending):
    """
    :param is_descending:Ob in absteigender Reihenfolge
    """
    return [sorted(i_input, key=lambda i: i[1], reverse=is_descending) for i_input in inputs]

Wenn dieser Vorgang ausgeführt wird,

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)

Eine in chronologischer Reihenfolge sortierte Liste wird wie unten gezeigt erstellt.

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')]]]

Erstellung von Seriendaten unter Berücksichtigung von Zeitreihen

Erstens ist der Prozess zum Erstellen von Serienmerkmalen variabler Länge (sequentielle Merkmale) aus der oben erstellten Liste wie folgt.

def create_sequential(inputs):
    #Löschen Sie die Liste der Zeitstempel in der Liste
    return [[i[0] for i in i_input] for i_input in inputs]

Wenn Sie dies tun,

print('Itemid (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat')
pprint.pprint(create_sequential(itemid_inputs))

print('Kategorie-ID (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat')
pprint.pprint(create_sequential(categoryid_inputs))

Sie können das gewünschte Ergebnis erzielen.

Itemid (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat
[[5, 3, 8, 4, 7, 4], 
 [6, 0, 7, 1, 8, 2], 
 [6, 7, 9, 5, 8, 5, 4, 3, 8]]

Kategorie-ID (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat
[[1, 1, 2, 1, 2, 1], 
 [2, 0, 2, 0, 2, 0], 
 [2, 2, 3, 1, 2, 1, 1, 1, 2]]

Erstellung von Kategoriedaten unter Berücksichtigung von Zeitreihen

Als Nächstes wird wie folgt vorgegangen, um den neuesten Datensatz jedes Benutzers als kategoriale Variable aus der oben erstellten Liste abzurufen.

def create_category(inputs, n=-1):
    """
    :param n:Welche Nummer soll in der chronologischen Liste aufbewahrt werden?
    """
    #Löschen Sie die Liste der Zeitstempel in der Liste
    #Belassen Sie nur die Daten der n-ten Reihe in chronologischer Reihenfolge
    return [[i[0] for i in i_input][n] for i_input in inputs]

Wenn Sie dies tun,

print('Die neueste Artikel-ID, die jeder Benutzer kontaktiert hat')
pprint.pprint(create_category(itemid_inputs, -1))

print('Die neueste Kategorie-ID, die jeder Benutzer kontaktiert hat')
pprint.pprint(create_category(categoryid_inputs, -1))

Sie können das gesuchte Ergebnis wie folgt erhalten:

Die neueste Artikel-ID, die jeder Benutzer kontaktiert hat
[4, 2, 8]

Die neueste Kategorie-ID, die jeder Benutzer kontaktiert hat
[1, 0, 2]

Zusammenfassung der Verarbeitung

Hier werden die für die obige Erklärung getrennten Funktionen wie folgt integriert.


def create_features(
        df, user_index_col, sort_col, target_col, user_num, is_descending, is_sequence, n=-1):
    """
    :param user_index_col:Benutzer-ID-Spalte
    :param sort_col:Spalte mit dem für die Sortierung verwendeten Wert
    :param target_col:Spalte, die Sie sortieren möchten
    :param user_num:Anzahl der Benutzer (vom Encoder usw. erhalten)
    :param is_descending:Ob in absteigender Reihenfolge
    :param is_sequence:Ob es sequentiell ist
    :param n:Welche Nummer soll in der chronologischen Liste bleiben (nur Kategorie)
    """
    #Liste erstellen
    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])

    #Sortierliste
    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]

So teilen und lesen Sie Daten

Hier wollte ich am meisten schreiben, wenn Sie eine Liste mit Zeitreiheninformationen wie oben beschrieben erstellen, wenn Sie nicht alle Daten im Speicher ablegen können, den Datensatz teilen und vor jeder Teilungseinheit lesen Es kann auch zur Verarbeitung verwendet werden.

Angenommen, der erste DataFrame ist in drei Teile unterteilt und wie unten gezeigt im Wörterbuch gespeichert. (Ich glaube nicht, dass es tatsächlich der Fall ist, aber als Beispiel ...)

{'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}

Da Zeitreiheninformationen in der Liste gespeichert sind, können sie verarbeitet werden, indem beispielsweise die Funktion wie folgt geändert wird.


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)]

    #Verarbeitung für jede Teilungseinheit des Datensatzes
    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]

Wenn Sie Folgendes tun,

print('Itemid (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat')
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('Die neueste Artikel-ID, die jeder Benutzer kontaktiert hat')
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))

Das Ergebnis ist das gleiche wie oben.

Itemid (in chronologischer Reihenfolge), die jeder Benutzer kontaktiert hat
[[5, 3, 8, 4, 7, 4], 
 [6, 0, 7, 1, 8, 2],
 [6, 7, 9, 5, 8, 5, 4, 3, 8]]

Die neueste Artikel-ID, die jeder Benutzer kontaktiert hat
 [4, 2, 8]

Sortieren nach anderen als Zeitreiheninformationen

Außerdem haben wir diesmal die Sortierkriterien auf Zeitreiheninformationen eingegrenzt, aber es ist auch möglich, nach anderen Spalten oder in absteigender Reihenfolge zu sortieren. Durch Ändern der für die obige Verarbeitung übergebenen Variablen sind aufsteigende / absteigende Reihenfolge und Sortierung durch Angabe von Spalten möglich.

Zum Beispiel im folgenden Datensatz

    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

Selbst wenn es eine Spalte namens Score gibt, ist der Vorgang wie folgt, wenn Sie Seriendaten und Kategoriedaten in absteigender Reihenfolge erstellen möchten.

print('Ergebnisreihenfolge (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('Maximale Punktzahl (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))

Das Ergebnis ist wie folgt.

Ergebnisreihenfolge (itemid)
[[3, 5, 3, 3, 4, 7], 
 [4, 0, 7, 5, 6, 8, 3, 8], 
 [0, 3, 1, 5, 2, 2, 4]]

Maximale Punktzahl (itemid)
[3, 4, 0]

Am Ende

Dieses Mal schrieb ich darüber, wie Protokolldaten speichersparend in Serien- und Kategoriefeatures konvertiert werden können, während Zeitreiheninformationen berücksichtigt werden. Wenn es einen besseren Weg gibt, lass es mich bitte in den Kommentaren wissen. Vielen Dank.

Recommended Posts

Speichersparende Konvertierung von Protokolldaten in die Merkmalsmenge der sequentiellen Kategorie unter Berücksichtigung von Zeitreihen
Speichersparende Matrixkonvertierung von Protokolldaten
Konvertierung von Zeitdaten in 25-Uhr-Notation
Zusammenfassung der Tools, die zum Analysieren von Daten in Python benötigt werden
Einfallsreichtum beim speichersparenden Umgang mit Daten mit Pandas
So erhalten Sie einen Überblick über Ihre Daten in Pandas
Django Geändert, um viele Daten gleichzeitig zu speichern
Ein Befehl zum Auflisten aller Dateien in der Reihenfolge des Dateinamens
Versuchen Sie, Merkmale von Sensordaten mit CNN zu extrahieren
So extrahieren Sie Funktionen von Zeitreihendaten mit PySpark Basics
Wie erstelle ich eine große Menge an Testdaten in MySQL? ??