Wenn Sie eine große Datenmenge mit dem Speicher Ihres PCs verarbeiten möchten, aber mit Pandas überleben möchten, fasse ich die Speichersparmethode und die damit verbundenen Ideen zusammen, die als letzter Beitrag verwendet werden können. Wenn Sie es regelmäßig bewusst verwenden, können Sie es analysieren, ohne Rechenressourcen zu verschwenden. Ich denke, es gibt verschiedene Vorteile. Wenn es jedoch ein Limit gibt und es nicht funktioniert, wird empfohlen, zunächst den Arbeitsspeicher zu erhöhen oder Cloud-Dienste wie AWS und GCP zu verwenden.
Dieses Mal werde ich die berühmten Titanic Survivor Prediction Data verwenden, da es nicht realistisch ist, nur die Methode zu schreiben. Die Datenmenge ist überhaupt nicht groß, aber bitte verzeihen Sie mir das. Wenn Sie sich nur Gedanken darüber machen, wie es geht, können Sie diesen Teil überspringen.
Betriebssystem: Windows 10 Home in einem billigen Notebook-PC auf dem Markt Umgebung: Jupyter-Notebook, das durch Mounten von Kaggle Docker auf dem oben genannten Betriebssystem gestartet wurde python:Python 3.6.6 :: Anaconda, Inc. Die Ordnerstruktur zum Zeitpunkt der Analyse lautet wie folgt: Der Ordner, in dem sich das Notizbuch befindet, und der Ordner, in dem sich die Nutzungsdaten befinden.
Legen Sie zuerst die heruntergeladenen Titanic-Daten in den Eingabeordner und bereiten Sie das Lesen der Daten vor. Führen Sie den folgenden Befehl auf dem Jupyter-Notebook aus.
#Bibliothek importieren
import os, gc, pickle, datetime, sys
import numpy as np
import pandas as pd
#Nutzungsdatenpfad und Datenbestätigung
INPUTPATH = '../input'
print(os.listdir(INPUTPATH))
#Ausführungsergebnis
#> ['gender_submission.csv', 'test.csv', 'train.csv']
Dann konnte ich die Daten bestätigen. Laden Sie train.csv.
df = pd.read_csv(f'{INPUTPATH}/train.csv')
df.head(1)
Wenn keine große Nachfrage nach maximaler, minimaler und gebrochener Genauigkeit von Daten besteht, können Sie den Speicherverbrauch reduzieren, indem Sie von Datentyp mit doppelter Genauigkeit zu einfacher Genauigkeit wechseln. Nehmen wir als Beispiel Verifizierungsdaten. Überprüfen Sie zunächst den geladenen Datentyp von train.csv. Sofern nicht anders angegeben, werden ganzzahlige Werte als int64 und Brüche als float64 gelesen.
df.dtypes
#Ausgabeergebnis unten
#PassengerId int64
#Survived int64
#Pclass int64
#Name object
#Sex object
#Age float64
#SibSp int64
#Parch int64
#Ticket object
#Fare float64
#Cabin object
#Embarked object
#dtype: object
Lassen Sie uns den Datentyp sofort ändern. .astype() Am einfachsten ist es, .astype () zu verwenden. Ändern wir beispielsweise den Preis für den Titanic-Ticketpreis von float64 in float32. Infolgedessen wird die Datengröße halbiert.
dfare1 = df.Fare.nbytes
print('Die Datengröße von Fare bei float64 ist:{:.2f} KB'.format(dfare1/1024))
dfare2 = df.Fare.astype('float32').nbytes
print('Die Datengröße von Fare bei float32 ist:{:.2f} KB'.format(dfare2/1024))
print('Durch Ändern des Datentyps{:.2f}%Ich konnte die Dateigröße reduzieren'.format(100*(1-dfare2/dfare1)))
##Ausgabeergebnis unten
#Die Datengröße von Fare bei float64 beträgt: 6.96 KB
#Die Datengröße von Fare bei float32 beträgt: 3.48 KB
#50 durch Ändern des Datentyps.00%Ich konnte die Dateigröße reduzieren
Beim Ändern des Datentyps muss jedoch sichergestellt werden, dass die Genauigkeit der Maximal-, Minimal- und Dezimalwerte der Originaldaten nicht analytisch beeinflusst wird. Beispielsweise ist im Fall des Float-Typs der Einfluss auf die Analyse vernachlässigbar, selbst wenn die Anzahl der Stellen nach dem Dezimalpunkt abnimmt, und im Fall des Int-Typs sollte der Bereich der Ganzzahlen fest berücksichtigt werden, selbst wenn der Datentyp geändert wird. wird gebraucht. Wenn nicht, ist dies ein extremes Beispiel. Wenn Sie jedoch den Datentyp ändern, obwohl der ursprüngliche Maximalwert der Spalte mit dem Namen PassengerId wie unten gezeigt 891 beträgt, ist dies der [Maximalwert, der durch den Datentyp ausgedrückt werden kann](https: //). Bitte beachten Sie, dass es bei docs.scipy.org/doc/numpy-1.10.0/user/basics.types.html bleibt und sich die Zahlen selbst ändern. Wenn Sie es nicht mit sehr kleinen oder großen Zahlen zu tun haben, ändern Sie zunächst die doppelte Genauigkeit (64 Bit) in die einfache Genauigkeit (32 Bit). Wenn sich dies nicht verbessert, überprüfen Sie den Zahlenbereich und die Genauigkeitsanforderungen des Dezimalpunkts einzeln und nehmen Sie einige Korrekturen vor. Alternativ können Sie hier ein Urteil fällen, indem Sie die später beschriebene Funktion direkt verwenden.
df.PassengerId.max()
#Ausgabeergebnis
#891
df.PassengerId.astype('int8').max()
#Ausgabeergebnis
#127
Es ist praktisch, read_csv von Pandas zu verwenden, da es eine Option namens dtype gibt, mit der Sie den Datentyp jeder Spalte beim Lesen von Daten angeben können. Dazu müssen Sie jedoch im Voraus ein Wörterbuch erstellen, das den Spaltennamen und den entsprechenden Datentyp definiert. Lesen Sie daher die Daten einmal, überprüfen Sie, ob das Ändern des Datentyps mit "df.describe ()" keine Auswirkungen hat, und verwenden Sie dann das Ergebnis von "df.dtypes" als Wörterbuch. Ersetzen Sie einfach 64 durch 32 und ersetzen Sie dtype durch np.dtype (bei der Anzeige wird es als dtype angezeigt, bei der Eingabe muss es jedoch np.dtype sein), und das Wörterbuch kann relativ einfach erstellt werden. kann machen.
dtype_dict=df.dtypes.to_dict()
print(dtype_dict)
dtype_dict ={'PassengerId': np.dtype('int32'),
'Survived': np.dtype('int32'),
'Pclass': np.dtype('int32'),
'Name': np.dtype('O'),
'Sex': np.dtype('O'),
'Age': np.dtype('float32'),
'SibSp': np.dtype('int32'),
'Parch': np.dtype('int32'),
'Ticket': np.dtype('O'),
'Fare': np.dtype('float32'),
'Cabin': np.dtype('O'),
'Embarked': np.dtype('O')}
df = pd.read_csv(f'{INPUTPATH}/train.csv',dtype=dtype_dict)
df.dtypes
##Ausgabeergebnis
#PassengerId int32
#Survived int32
#Pclass int32
#Name object
#Sex object
#Age float32
#SibSp int32
#Parch int32
#Ticket object
#Fare float32
#Cabin object
#Embarked object
#dtype: object
Sie können auch die praktischen Funktionen des Kaggle-Wettbewerbs (https://www.kaggle.com/fabiendaniel/elo-world) verwenden, die vor etwa einem Jahr verwendet wurden. Ich habe auch an diesem Wettbewerb teilgenommen und bin dankbar, dass ich ihn genutzt habe. Es wird auch in [diesem Artikel] vorgestellt (https://qiita.com/hiroyuki_kageyama/items/02865616811022f79754). Details finden Sie in diesem Artikel, aber sie treffen Entscheidungen basierend auf den Minimal- und Maximalwerten der Daten. Der folgende Code ist teilweise angepasst.
――Zunächst wird die doppelte Genauigkeit im Vergleich zum numerischen Typ geändert, dessen Datentyp nicht der Objekttyp ist.
#Der folgende Import ist erforderlich, wenn Sie die Funktion verwenden
from pandas.api.types import is_datetime64_any_dtype as is_datetime
from pandas.api.types import is_categorical_dtype
#Funktionsdefinition
def reduce_mem_usage(df, use_float16=False):
""" iterate through all the columns of a dataframe and modify the data type
to reduce memory usage.
"""
start_mem = df.memory_usage().sum() / 1024**2
print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))
for col in df.columns:
if is_datetime(df[col]) or is_categorical_dtype(df[col]):
# skip datetime type or categorical type
continue
col_type = df[col].dtype
if col_type != object:
c_min = df[col].min()
c_max = df[col].max()
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df[col] = df[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df[col] = df[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
df[col] = df[col].astype(np.int32)
elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
df[col] = df[col].astype(np.int64)
else:
if use_float16 and c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
df[col] = df[col].astype(np.float16)
elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
df[col] = df[col].astype(np.float32)
else:
df[col] = df[col].astype(np.float64)
#else:
#df[col] = df[col].astype('category')
end_mem = df.memory_usage().sum() / 1024**2
print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))
print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))
return df
Verwenden wir die Funktion. In diesem Fall könnte die Speichernutzung der geladenen train.csv um ca. 44% reduziert werden.
df = reduce_mem_usage(df, use_float16=False)
#Ausgabeergebnis
#Memory usage of dataframe is 0.08 MB
#Memory usage after optimization is: 0.05 MB
#Decreased by 43.7%
Das Ändern des Kategorietyps des letzten Punkts des benutzerdefinierten Teils der oben eingeführten Funktion redu_mem_usage () spart ebenfalls Speicherplatz. Selbst wenn Sie versuchen, das Geschlecht der Titanic-Daten vom Objekttyp in den Kategorietyp zu ändern, können Sie es in diesem Fall um etwa die Hälfte reduzieren.
dsex1 = df.Sex.nbytes
print('Die Datengröße von Fare zum Zeitpunkt des Objekts beträgt:{:.2f} KB'.format(dsex1/1024))
dsex2 = df.Fare.astype('category').nbytes
print('Die Datengröße des Tarifs zum Zeitpunkt der Kategorie beträgt:{:.2f} KB'.format(dsex2/1024))
print('Durch Ändern des Datentyps{:.2f}%Ich konnte die Dateigröße reduzieren'.format(100*(1-dsex2/dsex1)))
##Ausgabeergebnis
#Die Datengröße von Fare zum Zeitpunkt des Objekts beträgt: 6.96 KB
#Die Datengröße des Tarifs in der Kategorie beträgt: 3.68 KB
#Durch Ändern des Datentyps 47.17%Ich konnte die Dateigröße reduzieren
Es gibt jedoch einige Vorsichtsmaßnahmen beim Wechsel zum Kategorietyp. Wenn Werte fehlen, ist eine zusätzliche Verarbeitung erforderlich. Beispielsweise enthält die Kabinenspalte in der Passagierkabine fehlende Werte.
df.isnull().sum()
##Ausgabeergebnis
#PassengerId 0
#Survived 0
#Pclass 0
#Name 0
#Sex 0
#Age 177
#SibSp 0
#Parch 0
#Ticket 0
#Fare 0
#Cabin 687
#Embarked 2
#dtype: int64
Angenommen, Sie ändern dies in einen Kategorietyp und geben dann die fehlenden Werte ein. Dann erhalte ich die Fehlermeldung, dass es keine Kategorie gibt, die "null" entspricht und die ich durch "ValueError: Füllwert muss in Kategorien sein" ersetzen möchte.
df.Cabin = df.Cabin.astype('category')
df.Cabin = df.Cabin.fillna('null')
Sie müssen den Speicher nicht gründlich reduzieren. Wenn es sich bei dem Objekttyp um eine kleine Anzahl von Daten handelt, können Sie einfach den Objekttyp verwenden oder den Konvertierungsteil der Funktion redu_mem_usage () in den Kategorietyp weglassen. Es ist auch nicht immer möglich, den Speicher zu reduzieren, indem er zu einem Kategorietyp gemacht wird. Wenn Sie andererseits den Objekttyp in den Kategorietyp konvertieren und den Speicher gründlich reduzieren möchten, gibt es zwei Möglichkeiten, um den Fehler bei fehlender Wertvervollständigung zu beheben. Wenn Sie den fehlenden Wert im Voraus vervollständigen und dann wie in Methode 1 in den Kategorietyp konvertieren, tritt im Allgemeinen kein Fehler auf. Wenn Sie den fehlenden Wert jedoch nach Bedarf während der Analyse vervollständigen möchten, können Sie Schritt 2 verwenden. Es scheint effektiv zu sein, von einer Person zu antworten.
#1.die Methode von
df.Cabin = df.Cabin.fillna('null')
df.Cabin = df.Cabin.astype('category')
#2.die Methode von
df.Cabin = df.Cabin.cat.add_categories('null').fillna('null')
Verwenden Sie die Sparse Data Structure von pandas (https://pandas.pydata.org/pandas-docs/stable/user_guide/sparse.html), um den Speicher beim Codieren kategorialer Variablen in Dummy-Variablen zu reduzieren Es ist zu erwarten, dass dies einen Speicherreduzierungseffekt hat. Wenn die Anzahl der Spalten einer Dummy-Variablen, die nur aus 0 und 1 Daten besteht, zunimmt, kann der Speicherreduzierungseffekt durch Verwendung der Sparse Data Structure erzielt werden. In der Sparse Data Structure wird die Position einiger Daten mit 1 aufgezeichnet und die anderen 0 Teile werden komprimiert, ohne als Daten gespeichert zu werden. Es gibt jedoch einige Unannehmlichkeiten, die durch die Verwendung der Sparse Data Structure verursacht werden. Daher wird dies später beschrieben.
Lassen Sie uns vorher die kategorialen Variablen der Titanic-Daten mit einer großen Anzahl von Ebenen (hohe Kardinalität) auswählen und den Effekt sehen.
for var in df.dtypes[df.dtypes =='object'].index.tolist():
print('Unique level of '+var+' is {:}'.format(len(df[var].unique())))
#Ausgabeergebnis
#Unique level of Name is 891
#Unique level of Sex is 2
#Unique level of Ticket is 681
#Unique level of Cabin is 148
Unique level of Embarked is 4
Wie erwartet ist Name ein Name, also schließen wir ihn vom Ziel aus (lacht). Versuchen wir, Ticket, Cabin, Embarked zu codieren. Die Dummy-Codierung ist mit get_dummies () von pandas [https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.get_dummies.html] einfach.
Obwohl die Anzahl der Datenzeilen 891 beträgt, beträgt die Anzahl der Spalten 834, was erschreckend spärliche Daten sind. Die Speichernutzung beträgt ca. 726 KB, wenn sie mit df_dummies.info ()
überprüft wird. (Sie können die Datengröße auch in KB mit sys.getsizeof (df_dummies) / 1024
und df_dummies.memory_usage (). Sum () / 1024
abrufen.)
dummy_list = ['Ticket', 'Cabin', 'Embarked']
df_dummies = pd.get_dummies(df[dummy_list], dummy_na=True, sparse=False, prefix = dummy_list)
df_dummies.shape
##Ausgabeergebnis
#(891, 834)
df_dummies.info()
##Ausgabeergebnis
#<class 'pandas.core.frame.DataFrame'>
#RangeIndex: 891 entries, 0 to 890
#Columns: 834 entries, Ticket_110152 to Embarked_nan
#dtypes: uint8(834)
#memory usage: 725.8 KB
Versuchen Sie als Nächstes, Sparse Data Structure zu verwenden, um den Speicher zu reduzieren. get_dummies () hat eine Option namens Sparse, die normalerweise False ist, aber ändern Sie diese in True. Infolgedessen konnten wir den Speicher um etwa 98% von 726 KB auf 13 KB reduzieren.
df_dummies2 = pd.get_dummies(df[dummy_list], dummy_na=True, sparse=True, prefix = dummy_list)
df_dummies2.info()
##Ausgabeergebnis
#<class 'pandas.core.frame.DataFrame'>
#RangeIndex: 891 entries, 0 to 890
#Columns: 834 entries, Ticket_110152 to Embarked_nan
#dtypes: Sparse[uint8, 0](834)
#memory usage: 13.2 KB
Durch die Verwendung von Sparse Data Structure konnte ich den Speicher erheblich komprimieren. Es gibt jedoch etwas zu beachten. Der Punkt ist, dass die Komprimierung die Pandas-Methoden, die in normalen Datenstrukturen verwendet wurden, unbrauchbar machen kann. Angenommen, Sie haben eine Dummy-Variable namens Ticket_110152 und möchten die Anzahl der Einsen in allen Daten summieren. Bei einem normalen DataFrame ist .sum () ausreichend, bei Sparse Data Structure tritt jedoch ein Fehler auf, da die Daten komprimiert sind.
#Wie man es normal macht
df_dummies.Ticket_110152.sum()
#Ausgabeergebnis
#3
#Sparse Data Structure (Df wurde mit Sparse Data Structure erstellt_dummies2)
df_dummies2.Ticket_110152.sum()
#Ausgabeergebnis
#TypeError: sum() got an unexpected keyword argument 'min_count'
Um solche Fehler zu vermeiden, ist es besser, von der spärlichen Datenstruktur zur ursprünglichen Datenstruktur zurückzukehren. Dieses Mal ist es python3.6, also werde ich es mit np.asarray wiederherstellen, aber seit ptyhon3.7 kann es mit einer Methode namens .to_dense () einfacher gemacht werden.
np.asarray(df_dummies2.Ticket_110152).sum()
#Ausgabeergebnis
#3
#python 3.Nach 7
#df_dummies2.Ticket_110152.to_dense().sum()Aber du solltest gehen können
Hier finden Sie eine Zusammenfassung der Ideen, die Sie beim speichersparenden Umgang mit Pandas-Datenrahmen machen können, sowie die zu diesem Zeitpunkt zu beachtenden Punkte.
Vielen Dank für Ihren Besuch auf unserer Website. Wenn es einen anderen besseren Weg gibt, hinterlassen Sie bitte einen Kommentar. Es ist einfach, aber ich habe die Daten und den Code auf [github] hochgeladen (https://github.com/shinsei66/Reduce-memory-usage-in-padas-dataframe). Ich habe auch ein Beispiel einer Gruppe gepostet, indem ich Sparse Data Structure verwendet habe.