MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Grandes fluxos de trabalho de dados usando pandas


Eu uso rotineiramente dezenas de gigabytes de dados exatamente dessa forma. Eu tenho tabelas no disco que leio por meio de consultas, crio dados e adiciono de volta.

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

Detalhes que afetarão como você armazena seus dados, como:
Dê o máximo de detalhes que puder; e posso ajudá-lo a desenvolver uma estrutura.
  1. Tamanho dos dados, nº de linhas, colunas, tipos de colunas; você está anexando linhas ou apenas colunas?
  2. Como serão as operações típicas. Por exemplo. faça uma consulta em colunas para selecionar um monte de linhas e colunas específicas, depois faça uma operação (na memória), crie novas colunas, salve-as.
    (Dar um exemplo de brinquedo pode nos permitir oferecer recomendações mais específicas. )
  3. Depois desse processamento, o que você faz? A etapa 2 é ad hoc ou repetível?
  4. Arquivos simples de entrada:quantos, tamanho total aproximado em Gb. Como estes 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, selecione as linhas com campo A> 5)? e então fazer algo, ou você apenas seleciona os campos A, B, C com todos os registros (e então faz algo)?
  6. Você 'trabalha' em todas as suas colunas (em grupos) ou há 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 puxá-los coluna explicitamente até a hora dos resultados finais)?

Solução


Verifique se você tem pandas pelo menos 0.10.1 instalado.

Leia arquivos de iteração pedaço por pedaço e várias consultas de tabela.

Como pytables é otimizado para operar em linha (que é o que você consulta), criaremos uma tabela para cada grupo de campos. Desta forma é fácil selecionar um pequeno grupo de campos (que funcionará com uma tabela grande, mas é mais eficiente fazê-lo desta forma... Acho que posso corrigir 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_multiple faz):
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, você provavelmente teria que adicionar 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 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ê não precisa definir QUALQUER data_columns; eles permitem que você subselecione 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 que você obtenha 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 a criação de subconjuntos de linhas muito mais rápida).
  • ative a compactação.

Me avise quando tiver dúvidas!