Fluxos de trabalho de "dados grandes" usando pandas

982

Eu tentei descobrir uma resposta para essa pergunta por muitos meses enquanto aprendia pandas. Eu uso o SAS no meu trabalho diário e é ótimo pelo suporte fora do núcleo. No entanto, o SAS é horrível como um software por várias outras razões.

Um dia, espero substituir meu uso do SAS por python e pandas, mas atualmente não tenho um fluxo de trabalho fora do núcleo para grandes conjuntos de dados. Não estou falando de "big data" que requer uma rede distribuída, mas de arquivos muito grandes para caber na memória, mas pequenos o suficiente para caber em um disco rígido.

Meu primeiro pensamento é usar HDFStorepara armazenar grandes conjuntos de dados em disco e puxar apenas as peças necessárias para os quadros de dados para análise. Outros mencionaram o MongoDB como uma alternativa mais fácil de usar. Minha pergunta é esta:

Quais são alguns fluxos de trabalho de práticas recomendadas para realizar o seguinte:

  1. Carregando arquivos simples em uma estrutura de banco de dados permanente em disco
  2. Consultando esse banco de dados para recuperar dados para alimentar uma estrutura de dados do pandas
  3. Atualizando o banco de dados após manipular peças nos pandas

Exemplos do mundo real seriam muito apreciados, especialmente de quem usa pandas em "grandes dados".

Editar - um exemplo de como eu gostaria que isso funcionasse:

  1. Importe iterativamente um arquivo simples grande e armazene-o em uma estrutura de banco de dados permanente em disco. Esses arquivos geralmente são grandes demais para caber na memória.
  2. Para usar o Pandas, eu gostaria de ler subconjuntos desses dados (geralmente apenas algumas colunas por vez) que podem caber na memória.
  3. Eu criaria novas colunas executando várias operações nas colunas selecionadas.
  4. Eu teria que anexar essas novas colunas na estrutura do banco de dados.

Estou tentando encontrar uma maneira de praticar as melhores práticas para executar essas etapas. Lendo links sobre pandas e tabelas, parece que acrescentar uma nova coluna pode ser um problema.

Editar - Respondendo especificamente às perguntas de Jeff:

  1. Estou construindo modelos de risco de crédito ao consumidor. Os tipos de dados incluem características de telefone, SSN e endereço; valores de propriedade; informações depreciativas, como antecedentes criminais, falências, etc. Os conjuntos de dados que eu uso todos os dias têm cerca de 1.000 a 2.000 campos, em média, de tipos mistos de dados: variáveis ​​contínuas, nominais e ordinais de dados numéricos e de caracteres. Eu raramente anexo linhas, mas realizo muitas operações que criam novas colunas.
  2. As operações típicas envolvem a combinação de várias colunas usando lógica condicional em uma nova coluna composta. Por exemplo if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B',. O resultado dessas operações é uma nova coluna para cada registro no meu conjunto de dados.
  3. Por fim, gostaria de acrescentar essas novas colunas à estrutura de dados em disco. Eu repetiria a etapa 2, explorando os dados com tabelas cruzadas e estatísticas descritivas, tentando encontrar relacionamentos interessantes e intuitivos para modelar.
  4. Um arquivo de projeto típico costuma ter cerca de 1 GB. Os arquivos são organizados de tal maneira que uma linha consiste em um registro de dados do consumidor. Cada linha tem o mesmo número de colunas para cada registro. Este sempre será o caso.
  5. É muito raro eu agrupar por linhas ao criar uma nova coluna. No entanto, é bastante comum eu definir subconjuntos em linhas ao criar relatórios ou gerar estatísticas descritivas. Por exemplo, convém criar uma frequência simples para uma linha de negócios específica, como cartões de crédito de varejo. Para fazer isso, selecionaria apenas os registros em que a linha de negócios = varejo, além das colunas nas quais quero relatar. Ao criar novas colunas, no entanto, eu puxaria todas as linhas de dados e apenas as colunas necessárias para as operações.
  6. O processo de modelagem exige que eu analise todas as colunas, procure relacionamentos interessantes com alguma variável de resultado e crie novas colunas compostas que descrevam esses relacionamentos. As colunas que exploro são geralmente feitas em pequenos conjuntos. Por exemplo, vou me concentrar em um conjunto de 20 colunas, digamos, apenas lidando com valores de propriedades e observando como eles se relacionam com a inadimplência de um empréstimo. Depois que essas são exploradas e novas colunas são criadas, passo para outro grupo de colunas, digamos educação universitária e repito o processo. O que estou fazendo é criar variáveis ​​candidatas que explicam a relação entre meus dados e algum resultado. No final deste processo, aplico algumas técnicas de aprendizado que criam uma equação a partir dessas colunas compostas.

É raro que eu adicione linhas ao conjunto de dados. Eu quase sempre vou criar novas colunas (variáveis ​​ou recursos na estatística / linguagem de aprendizado de máquina).

Zelazny7
fonte
1
A proporção tamanho do núcleo / tamanho total é de 1%, 10%? Importa - se você pudesse compactar colunas para int8 ou filtrar linhas barulhentas, isso mudaria seu ciclo de pensamento de computação de dizer horas para minutos? (Também adicionar tag grande de dados.)
denis
1
Armazenando float32 vez de float64 e int8 sempre que possível, deve ser trivial (não sei o que ferramentas / funções fazer float64 internamente embora)
denis
você pode dividir sua tarefa em partes do trabalho?
Andrew Scott Evans
1
uma solução agradável 2019 para fazer pandas como operações sobre dados "média" que não cabem na memória é dask
lunguini
Existem alternativas para python + pandas que você pode querer considerar ver quando está começando. Considere o fato de que o Python é uma linguagem de programação de uso geral (não uma DSL para análise e análise de dados) e que o pandas é uma biblioteca abordada em cima disso. Eu consideraria olhar para R ou kdb.
Henry Henrinson 16/11/19

Respostas:

622

Uso rotineiramente dezenas de gigabytes de dados exatamente dessa maneira; por exemplo, tenho tabelas em disco que leio por meio de consultas, crio dados e anexo novamente.

Vale a pena ler os documentos e no final deste tópico para obter várias sugestões de como armazenar seus dados.

Detalhes que afetarão como você armazena seus dados, como:
Forneça o máximo de detalhes possível; e eu posso ajudá-lo a desenvolver uma estrutura.

  1. Tamanho dos dados, número de linhas, colunas, tipos de colunas; você está acrescentando linhas ou apenas colunas?
  2. Como serão as operações típicas. Por exemplo, faça uma consulta nas colunas para selecionar um monte de linhas e colunas específicas, depois faça uma operação (na memória), crie novas colunas e salve-as.
    (Dar um exemplo de brinquedo pode nos permitir oferecer recomendações mais específicas.)
  3. Após esse processamento, o que você faz? O passo 2 é ad hoc ou repetível?
  4. Arquivos simples de entrada: quantos, tamanho total aproximado em Gb. Como eles são organizados, por exemplo, por registros? Cada um contém campos diferentes ou eles têm alguns registros por arquivo com todos os campos em cada arquivo?
  5. Você já selecionou subconjuntos de linhas (registros) com base em critérios (por exemplo, seleciona as linhas com o campo A> 5)? e, em seguida, faça alguma coisa ou apenas selecione os campos A, B, C com todos os registros (e faça alguma coisa)?
  6. Você 'trabalha' em todas as suas colunas (em grupos) ou existe uma boa proporção que você pode usar apenas para relatórios (por exemplo, você deseja manter os dados por perto, mas não precisa extrair explicitamente a coluna até tempo dos resultados finais)?

Solução

Verifique se você tem pelo menos pandas0.10.1 instalados.

Leia arquivos iterativos, pedaço por pedaço, e várias consultas de tabela .

Como o pytables é otimizado para operar em linhas (que é o que você consulta), criaremos uma tabela para cada grupo de campos. Dessa forma, é fácil selecionar um pequeno grupo de campos (que funcionará com uma tabela grande, mas é mais eficiente fazê-lo dessa maneira ... Acho que posso consertar essa limitação no futuro ... mais intuitivo de qualquer maneira):
(O seguinte é pseudocódigo.)

import numpy as np
import pandas as pd

# create a store
store = pd.HDFStore('mystore.h5')

# this is the key to your storage:
#    this maps your fields to a specific group, and defines 
#    what you want to have as data_columns.
#    you might want to create a nice class wrapping this
#    (as you will want to have this map and its inversion)  
group_map = dict(
    A = dict(fields = ['field_1','field_2',.....], dc = ['field_1',....,'field_5']),
    B = dict(fields = ['field_10',......        ], dc = ['field_10']),
    .....
    REPORTING_ONLY = dict(fields = ['field_1000','field_1001',...], dc = []),

)

group_map_inverted = dict()
for g, v in group_map.items():
    group_map_inverted.update(dict([ (f,g) for f in v['fields'] ]))

Lendo os arquivos e criando o armazenamento (essencialmente fazendo o que append_to_multiplefaz):

for f in files:
   # read in the file, additional options may be necessary here
   # the chunksize is not strictly necessary, you may be able to slurp each 
   # file into memory in which case just eliminate this part of the loop 
   # (you can also change chunksize if necessary)
   for chunk in pd.read_table(f, chunksize=50000):
       # we are going to append to each table by group
       # we are not going to create indexes at this time
       # but we *ARE* going to create (some) data_columns

       # figure out the field groupings
       for g, v in group_map.items():
             # create the frame for this group
             frame = chunk.reindex(columns = v['fields'], copy = False)    

             # append it
             store.append(g, frame, index=False, data_columns = v['dc'])

Agora você tem todas as tabelas no arquivo (na verdade, você pode armazená-las em arquivos separados, se desejar, provavelmente adicionaria o nome do arquivo ao group_map, mas provavelmente isso não é necessário).

É assim que você obtém colunas e cria novas:

frame = store.select(group_that_I_want)
# you can optionally specify:
# columns = a list of the columns IN THAT GROUP (if you wanted to
#     select only say 3 out of the 20 columns in this sub-table)
# and a where clause if you want a subset of the rows

# do calculations on this frame
new_frame = cool_function_on_frame(frame)

# to 'add columns', create a new group (you probably want to
# limit the columns in this new_group to be only NEW ones
# (e.g. so you don't overlap from the other tables)
# add this info to the group_map
store.append(new_group, new_frame.reindex(columns = new_columns_created, copy = False), data_columns = new_columns_created)

Quando você estiver pronto para o pós-processamento:

# This may be a bit tricky; and depends what you are actually doing.
# I may need to modify this function to be a bit more general:
report_data = store.select_as_multiple([groups_1,groups_2,.....], where =['field_1>0', 'field_1000=foo'], selector = group_1)

Sobre data_columns, você realmente não precisa definir QUALQUER data_columns; eles permitem que você sub-selecione linhas com base na coluna. Por exemplo, algo como:

store.select(group, where = ['field_1000=foo', 'field_1001>0'])

Eles podem ser mais interessantes para você no estágio final de geração do relatório (essencialmente, uma coluna de dados é segregada de outras colunas, o que pode afetar um pouco a eficiência, se você definir muito).

Você também pode querer:

  • crie uma função que pegue uma lista de campos, procure os grupos no groups_map, selecione-os e concatene os resultados para obter o quadro resultante (isso é essencialmente o que select_as_multiple faz). Dessa forma, a estrutura seria bastante transparente para você.
  • índices em determinadas colunas de dados (torna o subconjunto de linhas muito mais rápido).
  • habilitar a compactação.

Avise-me quando tiver alguma dúvida!

Jeff
fonte
5
Obrigado pelos links. O segundo link me deixa um pouco preocupado por não poder acrescentar novas colunas às tabelas no HDFStore? Isso está correto? Além disso, adicionei um exemplo de como eu usaria essa configuração.
usar o seguinte código
4
A estrutura real no hdf depende de você. O Pytables é orientado a linhas, com colunas fixas no momento da criação. Você não pode anexar colunas depois que uma tabela é criada. No entanto, você pode criar uma nova tabela indexada da mesma forma que a tabela existente. (veja os exemplos select_as_multiple nos documentos). Dessa forma, você pode criar objetos de tamanho arbitrário e ter consultas bastante eficientes. A maneira como você usa os dados é fundamental para como eles devem ser organizados em disco. Envie-me um email fora da lista com pseudo-código de um exemplo mais específico.
Jeff
1
Atualizei minha pergunta para responder aos seus pontos detalhados. Vou trabalhar em um exemplo para enviá-lo para fora da lista. Obrigado!
usar o seguinte código
12
@ Jeff, com o Pandas em 0.17.x agora os problemas descritos acima foram resolvidos no Pandas?
ctrl-alt-delete
5
@ Jeff quer adicionar uma atualização rápida à sua resposta para promover o dask?
Boud 9/09/16
137

Eu acho que as respostas acima estão faltando uma abordagem simples que eu achei muito útil.

Quando tenho um arquivo muito grande para carregar na memória, divido o arquivo em vários arquivos menores (por linha ou coluna)

Exemplo: no caso de 30 dias no valor de dados comerciais com tamanho de ~ 30 GB, divido-os em um arquivo por dia com tamanho de ~ 1 GB. Posteriormente, processo cada arquivo separadamente e agrego os resultados no final

Uma das maiores vantagens é que ele permite o processamento paralelo dos arquivos (vários threads ou processos)

A outra vantagem é que a manipulação de arquivos (como adicionar / remover datas no exemplo) pode ser realizada por comandos regulares do shell, o que não é possível em formatos de arquivo mais avançados / complicados

Essa abordagem não abrange todos os cenários, mas é muito útil em muitos deles

user1827356
fonte
39
Acordado. Com todo o hype, é fácil esquecer que ferramentas de linha de comando pode ser 235x mais rápido do que um cluster Hadoop
zelusp
84

Agora, dois anos após a pergunta, existe um equivalente de panda 'fora do núcleo': dask . É excelente! Embora ele não suporte todas as funcionalidades dos pandas, você pode ir muito longe com isso.

Privado
fonte
6
e, para um exemplo totalmente elaborado com dask, basta dar uma olhada aqui stackoverflow.com/questions/37979167/…
#:
Dependendo dos seus dados, faz sentido dar uma olhada no pystore . Depende dask.
gies0r
66

Se seus conjuntos de dados tiverem entre 1 e 20 GB, você deverá obter uma estação de trabalho com 48 GB de RAM. Então o Pandas pode armazenar todo o conjunto de dados na RAM. Sei que não é a resposta que você procura aqui, mas fazer computação científica em um notebook com 4 GB de RAM não é razoável.

rjurney
fonte
7
"fazer computação científica em um notebook com 4 GB de RAM não é razoável" Defina razoável. Eu acho que a UNIVAC adotaria uma visão diferente. arstechnica.com/tech-policy/2011/09/…
grisaitis 26/08
2
Acordado! tente continuar trabalhando na memória, mesmo que custe $$ antecipadamente. Se o seu trabalho gerar um retorno financeiro, então, com o tempo, você recuperará as despesas através de sua maior eficiência.
ansonw
2
Fazer computação científica em uma estação de trabalho com 48 GB de RAM não é razoável.
Yaroslav Nikitenko #
4
@YaroslavNikitenko Um r4.2xlarge com 61GB / RAM custa $ 0,532 / hora. Que tipo de computação científica você está fazendo que não é tão valiosa? Parece incomum, se não irracional.
Rjurney #
4
@rjurney desculpe, talvez eu devesse ter excluído meu comentário. Seu julgamento sobre o computador científico "irracional" parece muito subjetivo. Faço meus cálculos científicos há anos em laptops, e isso me parece suficiente, porque na maioria das vezes escrevo código. Meus algoritmos são muito mais difíceis do ponto de vista da programação do que do computacional. Também tenho certeza de que, para escrever algoritmos escalonáveis, não se deve confiar nas limitações atuais de hardware. Seu comentário sobre a computação de outras pessoas pode parecer um pouco ofensivo (além da subjetividade), você se importaria de excluir essas poucas palavras?
Yaroslav Nikitenko
59

Eu sei que esse é um tópico antigo, mas acho que vale a pena conferir a biblioteca do Blaze . Ele foi criado para esse tipo de situação.

Dos documentos:

O Blaze estende a usabilidade do NumPy e Pandas à computação distribuída e fora do núcleo. O Blaze fornece uma interface semelhante à do NumPy ND-Array ou Pandas DataFrame, mas mapeia essas interfaces familiares em uma variedade de outros mecanismos computacionais, como Postgres ou Spark.

Edit: A propósito, é suportado pelo ContinuumIO e Travis Oliphant, autor de NumPy.

chishaku
fonte
Outra biblioteca que vale a pena examinar é o GraphLab Create: ele possui uma estrutura semelhante ao DataFrame eficiente que não é limitada pela capacidade da memória. blog.dato.com/…
waterproof
52

É o caso do pymongo. Também prototipei usando sql server, sqlite, HDF, ORM (SQLAlchemy) em python. Primeiro e acima de tudo, o pymongo é um banco de dados baseado em documento; portanto, cada pessoa seria um documento ( dictde atributos). Muitas pessoas formam uma coleção e você pode ter muitas coleções (pessoas, mercado de ações, renda).

pd.dateframe -> pymongo Nota: Eu uso o chunksizein read_csvpara mantê-lo entre 5 e 10k registros (o pymongo descarta o soquete, se maior)

aCollection.insert((a[1].to_dict() for a in df.iterrows()))

consulta: gt = maior que ...

pd.DataFrame(list(mongoCollection.find({'anAttribute':{'$gt':2887000, '$lt':2889000}})))

.find() retorna um iterador, então eu costumo usar ichunked para dividir em iteradores menores.

Que tal uma junção, já que normalmente consigo 10 fontes de dados para colar:

aJoinDF = pandas.DataFrame(list(mongoCollection.find({'anAttribute':{'$in':Att_Keys}})))

então (no meu caso, às vezes eu tenho que aJoinDFcomeçar primeiro antes de ser "mesclável".)

df = pandas.merge(df, aJoinDF, on=aKey, how='left')

E você pode gravar as novas informações em sua coleção principal através do método de atualização abaixo. (coleção lógica x fontes de dados físicas).

collection.update({primarykey:foo},{key:change})

Em pesquisas menores, apenas desnormalize. Por exemplo, você tem código no documento e apenas adiciona o texto do código do campo e faz uma dictpesquisa ao criar documentos.

Agora você tem um bom conjunto de dados baseado em uma pessoa, pode liberar sua lógica em cada caso e criar mais atributos. Finalmente, você pode ler em pandas seus 3 principais indicadores de memória máxima e fazer pivots / agg / exploração de dados. Isso funciona para mim por 3 milhões de registros com números / texto grande / categorias / códigos / flutuadores / ...

Você também pode usar os dois métodos criados no MongoDB (MapReduce e estrutura agregada). Consulte aqui para obter mais informações sobre a estrutura agregada , pois parece ser mais fácil que o MapReduce e parece útil para o trabalho agregado rápido. Observe que eu não precisava definir meus campos ou relações e posso adicionar itens a um documento. No estado atual do conjunto de ferramentas numpy, pandas e python em rápida mudança, o MongoDB me ajuda a começar a trabalhar :)

brian_the_bungler
fonte
Oi, eu estou brincando com o seu exemplo, como bem e eu me deparo com esse erro ao tentar inserir em um banco de dados: In [96]: test.insert((a[1].to_dict() for a in df.iterrows())) --------------- InvalidDocument: Cannot encode object: 0. Alguma idéia do que pode estar errado? Meu dataframe consiste em todos os tipos de int64 e é muito simples.
usar o seguinte código
2
Sim, eu fiz o mesmo para um intervalo simples DF e o int64 de numpy parece incomodar pymongo. Todos os dados com os quais eu joguei são convertidos de CSV (vs artificialmente via range ()) e têm tipos longos e, portanto, sem problemas. Em numpy você pode converter, mas eu vejo isso como prejudicial. Devo admitir que os itens 10.1 para HDF parecem emocionantes.
Brian_the_bungler
43

Vi isso um pouco tarde, mas trabalho com um problema semelhante (modelos de pagamento antecipado de hipotecas). Minha solução foi pular a camada HDFStore do pandas e usar tabelas de pitons retas. Eu salvo cada coluna como uma matriz HDF5 individual no meu arquivo final.

Meu fluxo de trabalho básico é primeiro obter um arquivo CSV do banco de dados. Eu fecho o zip, para que não seja tão grande. Em seguida, converto isso em um arquivo HDF5 orientado a linhas, repetindo-o em python, convertendo cada linha em um tipo de dados real e gravando-o em um arquivo HDF5. Isso leva algumas dezenas de minutos, mas não usa memória, pois está operando apenas linha por linha. Em seguida, "transponho" o arquivo HDF5 orientado a linhas para um arquivo HDF5 orientado a colunas.

A tabela de transposição se parece com:

def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
    # Get a reference to the input data.
    tb = h_in.getNode(table_path)
    # Create the output group to hold the columns.
    grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
    for col_name in tb.colnames:
        logger.debug("Processing %s", col_name)
        # Get the data.
        col_data = tb.col(col_name)
        # Create the output array.
        arr = h_out.createCArray(grp,
                                 col_name,
                                 tables.Atom.from_dtype(col_data.dtype),
                                 col_data.shape)
        # Store the data.
        arr[:] = col_data
    h_out.flush()

Lê-lo de volta então se parece com:

def read_hdf5(hdf5_path, group_path="/data", columns=None):
    """Read a transposed data set from a HDF5 file."""
    if isinstance(hdf5_path, tables.file.File):
        hf = hdf5_path
    else:
        hf = tables.openFile(hdf5_path)

    grp = hf.getNode(group_path)
    if columns is None:
        data = [(child.name, child[:]) for child in grp]
    else:
        data = [(child.name, child[:]) for child in grp if child.name in columns]

    # Convert any float32 columns to float64 for processing.
    for i in range(len(data)):
        name, vec = data[i]
        if vec.dtype == np.float32:
            data[i] = (name, vec.astype(np.float64))

    if not isinstance(hdf5_path, tables.file.File):
        hf.close()
    return pd.DataFrame.from_items(data)

Agora, eu geralmente executo isso em uma máquina com uma tonelada de memória; portanto, talvez não tenha cuidado o suficiente com o uso de memória. Por exemplo, por padrão, a operação de carregamento lê todo o conjunto de dados.

Isso geralmente funciona para mim, mas é um pouco desajeitado, e eu não posso usar a mágica fantasia das mesas de píer.

Edit: A vantagem real dessa abordagem, sobre o padrão de tabelas de tabelas de matriz de registros, é que eu posso carregar os dados no R usando o h5r, que não pode manipular tabelas. Ou, pelo menos, não consegui carregar tabelas heterogêneas.

Johann Hibschman
fonte
você se importaria de compartilhar comigo um pouco do seu código? Estou interessado em como você carrega os dados de algum formato de texto simples sem conhecer os tipos de dados antes de enviar para pytables. Além disso, parece que você trabalha apenas com dados de um tipo. Isso está correto?
Zelazny7
1
Antes de tudo, suponho que conheço os tipos de colunas antes do carregamento, em vez de tentar adivinhar a partir dos dados. Salvei um arquivo JSON "data spec" com os nomes e tipos de colunas e o uso ao processar os dados. (O arquivo geralmente é uma saída terrível do BCP, sem nenhum rótulo.) Os tipos de dados que eu uso são cadeias, flutuadores, números inteiros ou datas mensais. Eu transformo as seqüências de caracteres em ints, salvando uma tabela de enumeração e convertendo as datas em ints (meses após 2000), para que eu fique com ints e floats nos meus dados, além da enumeração. Agora eu salvo os carros alegóricos como float64, mas experimentei o float32.
Johann Hibschman 28/03
1
se você tiver tempo, os pls tentam compatibilidade externa com R: pandas.pydata.org/pandas-docs/dev/… , e se você tiver dificuldades, talvez possamos ajustá-lo
Jeff
Vou tentar, se puder. O rhdf5 é uma dor, já que é um pacote de biocondutores, em vez de apenas estar no CRAN como o h5r. Estou à mercê de nossa equipe de arquitetura técnica e houve um problema com o rhdf5 na última vez em que solicitei. De qualquer forma, parece um erro seguir a orientação de linha, em vez de a coluna, com uma loja OLAP, mas agora estou divagando.
Johann Hibschman
39

Um truque que achei útil para casos de uso de dados grandes é reduzir o volume dos dados, reduzindo a precisão da flutuação para 32 bits. Não é aplicável em todos os casos, mas em muitos aplicativos a precisão de 64 bits é um exagero e a economia de memória 2x vale a pena. Para tornar um ponto óbvio ainda mais óbvio:

>>> df = pd.DataFrame(np.random.randn(int(1e8), 5))
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float64(5)
memory usage: 3.7 GB

>>> df.astype(np.float32).info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float32(5)
memory usage: 1.9 GB
ytsaig
fonte
27

Como observado por outros, depois de alguns anos surgiu um equivalente de panda 'fora do núcleo': dask . Embora o dask não substitua os pandas e toda a sua funcionalidade, ele se destaca por vários motivos:

O Dask é uma biblioteca de computação paralela flexível para computação analítica que é otimizada para agendamento dinâmico de tarefas para cargas de trabalho computacionais interativas de coleções de “Big Data”, como matrizes paralelas, quadros de dados e listas que estendem interfaces comuns, como iteradores NumPy, Pandas ou Python para maiores ambientes distribuídos que a memória ou distribuídos e dimensiona de laptops a clusters.

Dask enfatiza as seguintes virtudes:

  • Familiar: Fornece array NumPy paralelo e objetos Pandas DataFrame
  • Flexível: fornece uma interface de agendamento de tarefas para mais cargas de trabalho personalizadas e integração com outros projetos.
  • Nativo: permite a computação distribuída no Pure Python com acesso à pilha PyData.
  • Rápido: Opera com baixa sobrecarga, baixa latência e serialização mínima necessária para algoritmos numéricos rápidos
  • Escala para cima: é executado de forma resiliente em clusters com milhares de núcleos Escala para baixo: trivial para configurar e executar em um laptop em um único processo
  • Responsivo: Projetado com a computação interativa em mente, fornece feedback e diagnóstico rápidos para ajudar os seres humanos

e para adicionar um exemplo de código simples:

import dask.dataframe as dd
df = dd.read_csv('2015-*-*.csv')
df.groupby(df.user_id).value.mean().compute()

substitui alguns códigos de pandas como este:

import pandas as pd
df = pd.read_csv('2015-01-01.csv')
df.groupby(df.user_id).value.mean()

e, especialmente digno de nota, fornece por meio da concurrent.futuresinterface uma infraestrutura geral para o envio de tarefas personalizadas:

from dask.distributed import Client
client = Client('scheduler:port')

futures = []
for fn in filenames:
    future = client.submit(load, fn)
    futures.append(future)

summary = client.submit(summarize, futures)
summary.result()
wp78de
fonte
Eu adicionei esta resposta desde que a postagem do @Private aparece regularmente na lista de sugestões para exclusão de conteúdo e tamanho.
wp78de
17

Vale mencionar aqui Ray também,
é uma estrutura de computação distribuída, que possui sua própria implementação para pandas de maneira distribuída.

Apenas substitua a importação dos pandas, e o código deve funcionar da seguinte maneira:

# import pandas as pd
import ray.dataframe as pd

#use pd as usual

pode ler mais detalhes aqui:

https://rise.cs.berkeley.edu/blog/pandas-on-ray/

lev
fonte
16

Mais uma variação

Muitas das operações realizadas no pandas também podem ser feitas como uma consulta db (sql, mongo)

O uso de um RDBMS ou mongodb permite executar algumas das agregações na Consulta ao DB (que é otimizada para grandes dados e usa cache e índices de forma eficiente)

Posteriormente, você pode executar o pós-processamento usando pandas.

A vantagem desse método é que você obtém otimizações do banco de dados para trabalhar com dados grandes, enquanto define a lógica em uma sintaxe declarativa de alto nível - e não precisa lidar com os detalhes de decidir o que fazer na memória e o que fazer de núcleo.

E embora a linguagem de consulta e os pandas sejam diferentes, geralmente não é complicado traduzir parte da lógica de uma para outra.

Ophir Yoktan
fonte
11

Considere o Ruffus se você seguir o caminho simples de criar um pipeline de dados dividido em vários arquivos menores.

Golf Monkey
fonte
9

Recentemente, deparei com um problema semelhante. Descobri que simplesmente ler os dados em pedaços e anexá-los à medida que escrevo em pedaços para o mesmo csv funciona bem. Meu problema foi adicionar uma coluna de data com base nas informações de outra tabela, usando o valor de determinadas colunas da seguinte maneira. Isso pode ajudar aqueles confusos com dask e hdf5, mas mais familiarizado com pandas como eu.

def addDateColumn():
"""Adds time to the daily rainfall data. Reads the csv as chunks of 100k 
   rows at a time and outputs them, appending as needed, to a single csv. 
   Uses the column of the raster names to get the date.
"""
    df = pd.read_csv(pathlist[1]+"CHIRPS_tanz.csv", iterator=True, 
                     chunksize=100000) #read csv file as 100k chunks

    '''Do some stuff'''

    count = 1 #for indexing item in time list 
    for chunk in df: #for each 100k rows
        newtime = [] #empty list to append repeating times for different rows
        toiterate = chunk[chunk.columns[2]] #ID of raster nums to base time
        while count <= toiterate.max():
            for i in toiterate: 
                if i ==count:
                    newtime.append(newyears[count])
            count+=1
        print "Finished", str(chunknum), "chunks"
        chunk["time"] = newtime #create new column in dataframe based on time
        outname = "CHIRPS_tanz_time2.csv"
        #append each output to same csv, using no header
        chunk.to_csv(pathlist[2]+outname, mode='a', header=None, index=None)
timpjohns
fonte
9

Eu gostaria de destacar o pacote Vaex.

O Vaex é uma biblioteca python para DataFrames Out-of-Core preguiçosos (semelhante ao Pandas), para visualizar e explorar grandes conjuntos de dados tabulares. Ele pode calcular estatísticas como média, soma, contagem, desvio padrão etc., em uma grade N-dimensional de até um bilhão (10 9 ) de objetos / linhas por segundo. A visualização é feita usando histogramas, gráficos de densidade e renderização de volume 3D, permitindo a exploração interativa de big data. O Vaex usa mapeamento de memória, política de cópia de memória zero e cálculos preguiçosos para obter o melhor desempenho (sem perda de memória).

Dê uma olhada na documentação: https://vaex.readthedocs.io/en/latest/ A API está muito próxima da API dos pandas.

Roubar
fonte
0

Por que pandas? Você já experimentou o Standard Python ?

O uso de python de biblioteca padrão. O Pandas está sujeito a atualizações frequentes, mesmo com o recente lançamento da versão estável.

Usando a biblioteca python padrão, seu código sempre será executado.

Uma maneira de fazer isso é ter uma idéia da maneira como você deseja que seus dados sejam armazenados e quais perguntas você deseja resolver em relação aos dados. Em seguida, desenhe um esquema de como você pode organizar seus dados (tabelas de reflexão) que o ajudarão a consultar os dados, não necessariamente a normalização.

Você pode fazer bom uso de:

  • lista de dicionários para armazenar os dados na memória, um ditado sendo uma linha,
  • geradores para processar os dados linha após linha para não sobrecarregar sua RAM,
  • compreensão da lista para consultar seus dados,
  • faça uso de Counter, DefaultDict, ...
  • armazene seus dados no disco rígido usando a solução de armazenamento escolhida, o json pode ser um deles.

Ram e HDD estão se tornando cada vez mais baratos com o tempo e o python 3 padrão está amplamente disponível e estável.

Pelicano
fonte
-1

No momento, estou trabalhando "como" você, apenas em uma escala mais baixa, e é por isso que não tenho um PoC para minha sugestão.

No entanto, parece que tenho sucesso em usar pickle como sistema de cache e terceirizar a execução de várias funções em arquivos - executando esses arquivos a partir do meu comando / arquivo principal; Por exemplo, eu uso um prepare_use.py para converter tipos de objetos, dividir um conjunto de dados em teste, validação e conjunto de dados de previsão.

Como funciona seu cache com pickle? Eu uso seqüências de caracteres para acessar arquivos pickle criados dinamicamente, dependendo de quais parâmetros e conjuntos de dados foram passados ​​(com isso, tento capturar e determinar se o programa já foi executado, usando .shape para conjunto de dados, ditado para passado parâmetros). Respeitando essas medidas, recebo uma String para tentar encontrar e ler um arquivo .pickle e, se encontrado, pode pular o tempo de processamento para pular para a execução na qual estou trabalhando agora.

Usando bancos de dados, encontrei problemas semelhantes, e foi por isso que senti alegria ao usar esta solução; no entanto - há muitas restrições - por exemplo, armazenar grandes conjuntos de picles devido à redundância. A atualização de uma tabela de antes para depois de uma transformação pode ser feita com a indexação adequada - as informações de validação abrem outro livro (tentei consolidar dados de aluguel rastreados e parei de usar um banco de dados após 2 horas basicamente - como eu gostaria de voltar depois todo processo de transformação)

Espero que meus 2 centavos o ajudem de alguma forma.

Saudações.

TiRoX
fonte