Como estimar quanta memória um DataFrame do Pandas vai precisar?

125

Eu estive pensando ... Se eu estiver lendo, digamos, um arquivo csv de 400 MB em um dataframe do pandas (usando read_csv ou read_table), há alguma maneira de estimar quanta memória isso vai precisar? Só tentando ter uma ideia melhor dos frames de dados e da memória ...

Anne
fonte
Você sempre pode olhar para o processo e seu uso de memória para um único arquivo. Se você estiver executando o Linux, tente tope, em seguida, Shift + Mpara classificar o meu uso de memória.
JayQuerie.com
Acho que devo anunciar essa edição dos pandas abertos .
Andy Hayden
3
Eu tenho um grande dataframe com 4 milhões de linhas. Descobri que seu subconjunto vazio x=df.loc[[]]leva 0.1segundos para ser calculado (para extrair zero linhas) e, além disso, leva centenas de megabytes de memória, assim como o dataframe original, provavelmente por causa de alguma cópia embaixo.
osa
novo link para a postagem antiga do desenvolvedor líder do pandas
saladi

Respostas:

97

df.memory_usage() retornará quanto cada coluna ocupa:

>>> df.memory_usage()

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

Para incluir índices, passe index=True.

Portanto, para obter o consumo geral de memória:

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

Além disso, a passagem deep=Truepermitirá um relatório de uso de memória mais preciso, que contabiliza o uso total dos objetos contidos.

Isso ocorre porque o uso de memória não inclui a memória consumida por elementos que não são componentes da matriz if deep=False(caso padrão).

Aleksey Sivokon
fonte
1
é a soma de todos os usos de memória das colunas realmente o impacto no uso de memória? Posso imaginar que haja mais sobrecarga.
firelynx
14
Você realmente também querdeep=True
smci
A soma de df.memory_usage () não é igual a sys.getsizeof (df)! Existem muitas despesas gerais. Como smci mencionou, você precisadeep=True
vagabundo
11
FYI, memory_usage()retorna o uso de memória em bytes (como você esperaria).
engelen
2
Por que uma diferença tão grande entre com / sem deep = True?
Nguai al
83

Aqui está uma comparação dos diferentes métodos - sys.getsizeof(df)é o mais simples.

Para este exemplo, dfé um dataframe com 814 linhas, 11 colunas (2 ints, 9 objetos) - lido a partir de um shapefile de 427kb

sys.getsizeof (df)

>>> import sys
>>> sys.getsizeof (df)
(dá resultados em bytes)
462456

df.memory_usage ()

>>> df.memory_usage ()
...
(lista cada coluna em 8 bytes / linha)

>>> df.memory_usage (). sum ()
71712
(aproximadamente linhas * cols * 8 bytes)

>>> df.memory_usage (deep = True)
(lista o uso total de memória de cada coluna)

>>> df.memory_usage (deep = True) .sum ()
(dá resultados em bytes)
462432

df.info ()

Imprime informações do dataframe no stdout. Tecnicamente, são kibibytes (KiB), não kilobytes - como diz a docstring, "O uso da memória é mostrado em unidades legíveis (representação de base 2)." Portanto, obter bytes seria multiplicado por 1024, por exemplo, 451,6 KiB = 462.438 bytes.

>>> df.info ()
...
uso de memória: 70,0+ KB

>>> df.info (memory_usage = 'deep')
...
uso de memória: 451,6 KB
Brian Burns
fonte
A qual objeto ou módulo o g código acima se refere?
zozo
@zozo woops - foi um erro de digitação - corrigido
Brian Burns
2
Eu uso df.info(memory_usage="deep"), ele retorna "392,6 MB", enquanto sys.getsizeof(df)e df.memory_usage(index=True, deep=True).sum()ambos retornam aproximadamente "411718016" (~ 411 MB). Você pode explicar por que os 3 resultados não são consistentes? obrigado
Catbuilts
2
@BrianBurns: df.memory_usage(deep=True).sum()retorna quase o mesmo com df.memory_usage(index=True, deep=True).sum(). no meu caso, indexnão ocupa muita memória. Curiosamente, descobri que 411718016/1024/1024 = 392.6, portanto, df.info(memory_usage="deep")pode usar 2^10para converter byte em MB , o que me deixa confuso. Obrigado pela sua ajuda de qualquer maneira: D.
Catbuilts
1
@Catbuilts Ah, isso explica tudo! df.infoestá retornando mebibytes (2 ^ 10), não megabytes (10 ^ 6) - corrigirá a resposta.
Brian Burns
43

Pensei em trazer mais alguns dados para a discussão.

Executei uma série de testes sobre esse problema.

Usando o resourcepacote python , consegui o uso de memória do meu processo.

E escrevendo o csv em um StringIObuffer, eu poderia facilmente medir o tamanho dele em bytes.

Fiz dois experimentos, cada um criando 20 dataframes de tamanhos crescentes entre 10.000 e 1.000.000 de linhas. Ambos com 10 colunas.

No primeiro experimento, usei apenas flutuadores em meu conjunto de dados.

É assim que a memória aumentou em comparação ao arquivo csv em função do número de linhas. (Tamanho em megabytes)

Memória e tamanho do CSV em megabytes em função do número de linhas com entradas flutuantes

No segundo experimento, tive a mesma abordagem, mas os dados no conjunto de dados consistiam em apenas sequências curtas.

Tamanho da memória e CSV em megabytes em função do número de linhas com entradas de string

Parece que a relação entre o tamanho do csv e o tamanho do dataframe pode variar bastante, mas o tamanho na memória será sempre maior por um fator de 2-3 (para os tamanhos de quadro neste experimento)

Eu adoraria completar esta resposta com mais experimentos, por favor, comente se você quiser que eu tente algo especial.

firelynx
fonte
Qual é o seu eixo y?
Ilya V. Schurov
1
tamanho max_rss e csv no disco em megabytes
firelynx
31

Você tem que fazer isso ao contrário.

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

Tecnicamente, a memória é sobre isso (que inclui os índices)

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

Portanto, 168 MB de memória com um arquivo de 400 MB, 1 milhão de linhas de 20 colunas flutuantes

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

MUITO mais compacto quando escrito como um arquivo HDF5 binário

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

Os dados eram aleatórios, então a compressão não ajuda muito

Jeff
fonte
Isso é muito inteligente! Alguma ideia de como medir a memória que você precisa para ler o arquivo usando read_csv?
Andy Hayden
Não tenho ideia de como medir o AS que você lê; IIRC pode ser até 2x a memória final necessária para manter os dados (do artigo de wes), mas acho que ele reduziu para uma memória constante + final
Jeff
Ah, preciso reler, lembrei-me de que 2x é um mínimo teórico conveniente para um determinado algoritmo, se é ainda menos legal.
Andy Hayden
Você pode usar iotoplike top/ htoppara assistir (em tempo real) o desempenho de IO.
Phillip Cloud
1
nbytesserá uma estimativa grosseira se você tiver, por exemplo, strings em um dataframe.
osa
10

Se você conhece os dtypes do seu array, então pode calcular diretamente o número de bytes necessários para armazenar seus dados + alguns para os próprios objetos Python. Um atributo útil dos numpyarrays é nbytes. Você pode obter o número de bytes dos arrays em um pandas DataFramefazendo

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

objectArrays dtype armazenam 8 bytes por objeto (arrays dtype de objetos armazenam um ponteiro para um opaco PyObject), então se você tem strings em seu csv você precisa levar em consideração que read_csvirá transformá-los emobject arrays dtype e ajustar seus cálculos de acordo.

EDITAR:

Consulte a numpypágina de tipos escalares para obter mais detalhes sobre o object dtype. Visto que apenas uma referência é armazenada, você também precisa levar em consideração o tamanho do objeto no array. Como diz a página, os arrays de objetos são um tanto semelhantes aos listobjetos Python .

Phillip Cloud
fonte
Obrigado Phillip! Só para esclarecer - para uma string, precisaríamos de 8 bytes para um ponteiro para um objeto de string, mais o objeto de string real?
Anne
1
Sim, para qualquer tipo de objeto, você precisará de um ponteiro de 8 bytes + tamanho (objeto)
Viktor Kerkez
1
Sugerir df.blocks.values ​​() Parece que df.blocks agora é um dict
MRocklin
8

Sim existe. O Pandas irá armazenar seus dados em ndarrayestruturas numpy bidimensionais agrupando-os por dtypes. ndarrayé basicamente uma matriz de dados C bruta com um pequeno cabeçalho. Assim, você pode estimar seu tamanho simplesmente multiplicando o tamanho do dtypeque contém pelas dimensões da matriz.

Por exemplo: se você tiver 1000 linhas com 2 np.int32e 5 np.float64colunas, seu DataFrame terá uma np.int32matriz 2x1000 e uma np.float64matriz 5x1000 que é:

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

Viktor Kerkez
fonte
@AndyHayden O que você quer dizer com custo de construção? O tamanho de uma instância de DataFrame?
Phillip Cloud de
Obrigado Victor! @Andy - Tem ideia de quão grande é o custo de construção?
Anne
Não está incluído, mas pandastem uma implementação muito eficiente do read_tableno Cython (é muito melhor do que o loadtxt do numpy), então presumo que ele analisa e armazena os dados diretamente no ndarray.
Viktor Kerkez
@PhillipCloud você tem que construí-lo, isso leva memória .. Eu me lembro do dobro do tamanho mencionado? ...
Andy Hayden
6

Isso eu acredito que dá o tamanho na memória de qualquer objeto em python. Os internos precisam ser verificados em relação aos pandas e entorpecidos

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