Comment estimer la quantité de mémoire dont un DataFrame Pandas aura besoin?

126

Je me suis demandé ... Si je lis, disons, un fichier csv de 400 Mo dans un dataframe pandas (en utilisant read_csv ou read_table), y a-t-il un moyen d'estimer la quantité de mémoire dont il aura besoin? J'essaie simplement d'avoir une meilleure idée des trames de données et de la mémoire ...

Anne
la source
Vous pouvez toujours regarder le processus et son utilisation de la mémoire pour un seul fichier. Si vous utilisez Linux, essayez toppuis Shift + Mde trier mon utilisation de la mémoire.
JayQuerie.com
Je pense que je devrais annoncer ce problème de pandas ouvert .
Andy Hayden
3
J'ai une grande trame de données avec 4 millions de lignes. J'ai découvert que son sous-ensemble vide x=df.loc[[]]prend 0.1quelques secondes à être calculé (pour extraire zéro ligne) et, en outre, prend des centaines de mégaoctets de mémoire, tout comme la trame de données d'origine, probablement à cause d'une copie en dessous.
osa
nouveau lien pour l' ancien message par le développeur principal de pandas
saladi

Réponses:

98

df.memory_usage() renverra combien chaque colonne occupe:

>>> df.memory_usage()

Row_ID            20906600
Household_ID      20906600
Vehicle           20906600
Calendar_Year     20906600
Model_Year        20906600
...

Pour inclure des index, passez index=True.

Donc, pour obtenir la consommation globale de mémoire:

>>> df.memory_usage(index=True).sum()
731731000

En outre, le passage deep=Truepermettra un rapport d'utilisation de la mémoire plus précis, qui rend compte de l'utilisation complète des objets contenus.

Cela est dû au fait que l'utilisation de la mémoire n'inclut pas la mémoire consommée par des éléments qui ne sont pas des composants du tableau if deep=False(cas par défaut).

Aleksey Sivokon
la source
1
la somme des utilisations de la mémoire de toutes les colonnes a-t-elle vraiment un impact sur l'utilisation de la mémoire? Je peux imaginer qu'il y ait plus de frais généraux.
firelynx
14
Vous voulez vraiment aussideep=True
smci
La somme de df.memory_usage () n'est pas égale à sys.getsizeof (df)! Il y a de nombreux frais généraux. Comme mentionné par smci, vous avez besoin dedeep=True
vagabond
11
FYI, memory_usage()renvoie l'utilisation de la mémoire en octets (comme vous vous en doutez).
engelen
2
Pourquoi une telle différence entre avec / sans deep = True?
Nguai al
83

Voici une comparaison des différentes méthodes - sys.getsizeof(df)c'est le plus simple.

Pour cet exemple, dfest un dataframe avec 814 lignes, 11 colonnes (2 pouces, 9 objets) - lu à partir d'un fichier de formes de 427 Ko

sys.getsizeof (df)

>>> import sys
>>> sys.getsizeof (df)
(donne les résultats en octets)
462456

df.memory_usage ()

>>> df.memory_usage ()
...
(répertorie chaque colonne à 8 octets / ligne)

>>> df.memory_usage (). sum ()
71712
(environ lignes * cols * 8 octets)

>>> df.memory_usage (deep = True)
(répertorie l'utilisation complète de la mémoire de chaque colonne)

>>> df.memory_usage (deep = True) .sum ()
(donne les résultats en octets)
462432

df.info ()

Imprime les informations de la trame de données sur stdout. Techniquement, ce sont des kibioctets (Kio), pas des kilo-octets - comme le dit la docstring, "l'utilisation de la mémoire est indiquée en unités lisibles par l'homme (représentation en base 2)." Donc, obtenir des octets serait multiplié par 1024, par exemple 451,6 Kio = 462 438 octets.

>>> df.info ()
...
utilisation de la mémoire: 70,0+ Ko

>>> df.info (memory_usage = 'profond')
...
utilisation de la mémoire: 451,6 Ko
Brian Burns
la source
À quel objet ou module le g code ci-dessus fait-il référence?
zozo
@zozo woops - était une faute de frappe - corrigé
Brian Burns
2
J'utilise df.info(memory_usage="deep"), il renvoie "392,6 Mo", alors sys.getsizeof(df)que les df.memory_usage(index=True, deep=True).sum()deux renvoient environ "411718016" (~ 411 Mo). Pouvez-vous expliquer pourquoi les 3 résultats ne sont pas cohérents? merci
Catbuilts
2
@BrianBurns: df.memory_usage(deep=True).sum()renvoie presque la même chose avec df.memory_usage(index=True, deep=True).sum(). dans mon cas, le ne indexprend pas beaucoup de mémoire. Chose intéressante, j'ai trouvé cela 411718016/1024/1024 = 392.6, donc df.info(memory_usage="deep")peut-être utilisé 2^10pour convertir des octets en Mo , ce qui me rend confus. Merci pour votre aide quand même: D.
Catbuilts
1
@Catbuilts Ah, ça explique! df.inforenvoie des mégaoctets (2 ^ 10), pas des mégaoctets (10 ^ 6) - modifiera la réponse.
Brian Burns
43

J'ai pensé que j'apporterais plus de données à la discussion.

J'ai effectué une série de tests sur ce problème.

En utilisant le resourcepackage python , j'ai obtenu l'utilisation de la mémoire de mon processus.

Et en écrivant le csv dans un StringIOtampon, je pourrais facilement en mesurer la taille en octets.

J'ai mené deux expériences, chacune créant 20 dataframes de tailles croissantes entre 10 000 lignes et 1 000 000 lignes. Les deux ayant 10 colonnes.

Dans la première expérience, j'ai utilisé uniquement des flottants dans mon ensemble de données.

C'est ainsi que la mémoire a augmenté par rapport au fichier csv en fonction du nombre de lignes. (Taille en mégaoctets)

Mémoire et taille CSV en mégaoctets en fonction du nombre de lignes avec des entrées flottantes

La deuxième expérience, j'ai eu la même approche, mais les données de l'ensemble de données ne consistaient que de courtes chaînes.

Mémoire et taille CSV en mégaoctets en fonction du nombre de lignes avec des entrées de chaîne

Il semble que la relation entre la taille du csv et la taille de la trame de données peut varier beaucoup, mais la taille en mémoire sera toujours plus grande d'un facteur 2-3 (pour les tailles de trame de cette expérience)

J'aimerais compléter cette réponse avec plus d'expériences, veuillez commenter si vous voulez que j'essaye quelque chose de spécial.

firelynx
la source
Quel est votre axe y?
Ilya V. Schurov
1
taille max_rss et csv sur le disque en mégaoctets
firelynx
31

Vous devez le faire à l'envers.

In [4]: DataFrame(randn(1000000,20)).to_csv('test.csv')

In [5]: !ls -ltr test.csv
-rw-rw-r-- 1 users 399508276 Aug  6 16:55 test.csv

Techniquement, la mémoire est à ce sujet (qui comprend les index)

In [16]: df.values.nbytes + df.index.nbytes + df.columns.nbytes
Out[16]: 168000160

Donc 168 Mo en mémoire avec un fichier de 400 Mo, 1M lignes de 20 colonnes flottantes

DataFrame(randn(1000000,20)).to_hdf('test.h5','df')

!ls -ltr test.h5
-rw-rw-r-- 1 users 168073944 Aug  6 16:57 test.h5

BEAUCOUP plus compact lorsqu'il est écrit sous forme de fichier binaire HDF5

In [12]: DataFrame(randn(1000000,20)).to_hdf('test.h5','df',complevel=9,complib='blosc')

In [13]: !ls -ltr test.h5
-rw-rw-r-- 1 users 154727012 Aug  6 16:58 test.h5

Les données étaient aléatoires, donc la compression n'aide pas trop

Jeff
la source
C'est très intelligent! Une idée comment mesurer la mémoire dont vous avez besoin pour lire le fichier en utilisant read_csv?
Andy Hayden
Aucune idée de comment mesurer pendant que vous lisez; IIRC, cela peut représenter jusqu'à 2 fois la mémoire finale nécessaire pour contenir les données (de l'article de wes), mais je pense qu'il l'a ramenée à une mémoire constante + finale
Jeff
Ah, j'ai besoin de relire, je me suis souvenu que 2x était un minimum théorique pratique pour un certain algorithme, si c'est encore moins coool.
Andy Hayden
Vous pouvez utiliser iotoplike top/ htoppour regarder (en temps réel) les performances d'E / S.
Phillip Cloud
1
nbytessera une sous-estimation grossière si vous avez par exemple des chaînes dans un dataframe.
osa
10

Si vous connaissez les dtypes de votre tableau, vous pouvez directement calculer le nombre d'octets qu'il faudra pour stocker vos données + certains pour les objets Python eux-mêmes. Un attribut utile des numpytableaux est nbytes. Vous pouvez obtenir le nombre d'octets des tableaux d'un pandas DataFrameen faisant

nbytes = sum(block.values.nbytes for block in df.blocks.values())

objectLes tableaux dtype stockent 8 octets par objet (les tableaux d'objet dtype stockent un pointeur vers un opaque PyObject), donc si vous avez des chaînes dans votre csv, vous devez prendre en compte ce read_csvqui les transformera en objecttableaux dtype et ajuster vos calculs en conséquence.

ÉDITER:

Consultez la numpypage des types scalaires pour plus de détails sur le object dtype. Comme seule une référence est stockée, vous devez également prendre en compte la taille de l'objet dans le tableau. Comme le dit cette page, les tableaux d'objets sont quelque peu similaires aux listobjets Python .

Phillip Cloud
la source
Merci Phillip! Juste pour clarifier - pour une chaîne, nous aurions besoin de 8 octets pour un pointeur vers un objet chaîne, plus l'objet chaîne réel?
Anne
1
Oui, pour tout type d'objet, vous aurez besoin d'un pointeur de 8 octets + taille (objet)
Viktor Kerkez
1
Suggérer df.blocks.values ​​() Il semble que df.blocks est maintenant un dict
MRocklin
8

Oui il y a. Pandas stockera vos données dans des ndarraystructures numpy à 2 dimensions en les regroupant par dtypes. ndarrayest fondamentalement un tableau de données C brut avec un petit en-tête. Vous pouvez donc estimer sa taille simplement en multipliant la taille de celui dtypequ'il contient par les dimensions du tableau.

Par exemple: si vous avez 1000 lignes avec 2 np.int32et 5 np.float64colonnes, votre DataFrame aura un np.int32tableau 2x1000 et un 5x1000np.float64 tableau qui est:

4 octets * 2 * 1000 + 8 octets * 5 * 1000 = 48000 octets

Viktor Kerkez
la source
@AndyHayden Que voulez-vous dire par le coût de construction? La taille d'une instance de DataFrame?
Phillip Cloud
Merci Victor! @Andy - Une idée du coût de construction?
Anne
Il n'inclut pas, mais pandasa une implémentation très efficace read_tableen Cython (c'est beaucoup mieux que le loadtxt de numpy), donc je suppose qu'il analyse et stocke les données directement dans le fichier ndarray.
Viktor Kerkez
@PhillipCloud vous devez le construire, cela prend de la mémoire .. Je semble me souvenir du double de la taille mentionnée? ...
Andy Hayden
6

Je crois que cela donne la taille en mémoire de tout objet en python. Les éléments internes doivent être vérifiés en ce qui concerne les pandas et numpy

>>> import sys
#assuming the dataframe to be df 
>>> sys.getsizeof(df) 
59542497
Zaher Abdul Azeez
la source