MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Flux de travail de données volumineux utilisant des pandas

J'utilise régulièrement des dizaines de gigaoctets de données de cette manière, par exemple. J'ai des tables sur le disque que je lis via des requêtes, crée des données et les rajoute.

Il vaut la peine de lire la documentation et plus tard dans ce fil de discussion pour plusieurs suggestions sur la façon de stocker vos données.

Détails qui affecteront la façon dont vous stockez vos données, comme :
Donnez autant de détails que possible ; et je peux vous aider à développer une structure.

  1. Taille des données, nombre de lignes, colonnes, types de colonnes ; ajoutez-vous des lignes ou seulement des colonnes ?
  2. À quoi ressembleront les opérations typiques ? Par exemple. effectuez une requête sur des colonnes pour sélectionner un groupe de lignes et de colonnes spécifiques, puis effectuez une opération (en mémoire), créez de nouvelles colonnes, enregistrez-les.
    (Donnez un exemple jouet pourrait nous permettre de proposer des recommandations plus spécifiques. )
  3. Après ce traitement, que faites-vous ? L'étape 2 est-elle ponctuelle ou reproductible ?
  4. Fichiers plats d'entrée :combien, taille totale approximative en Go. Comment sont-ils organisés, par ex. par des enregistrements ? Chacun contient-il des champs différents, ou y a-t-il des enregistrements par fichier avec tous les champs de chaque fichier ?
  5. Vous arrive-t-il de sélectionner des sous-ensembles de lignes (enregistrements) en fonction de critères (par exemple, sélectionner les lignes avec le champ A > 5) ? puis faites quelque chose, ou sélectionnez-vous simplement les champs A, B, C avec tous les enregistrements (puis faites quelque chose) ?
  6. Travaillez-vous sur toutes vos colonnes (en groupes) ou y a-t-il une bonne proportion que vous ne pouvez utiliser que pour les rapports (par exemple, vous souhaitez conserver les données, mais vous n'avez pas besoin de les extraire colonne explicitement jusqu'au moment des résultats finaux) ?

Solution

Assurez-vous d'avoir des pandas au moins 0.10.1 installé.

Lire les fichiers itératifs morceau par morceau et plusieurs requêtes de table.

Étant donné que pytables est optimisé pour fonctionner par ligne (ce sur quoi vous interrogez), nous allons créer une table pour chaque groupe de champs. De cette façon, il est facile de sélectionner un petit groupe de champs (ce qui fonctionnera avec une grande table, mais c'est plus efficace de le faire de cette façon... Je pense que je pourrai peut-être corriger cette limitation à l'avenir... c'est plus intuitif de toute façon):
(Ce qui suit est un pseudocode.)

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'] ]))

Lecture des fichiers et création du stockage (essentiellement en faisant ce que append_to_multiple fait):

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'])

Vous avez maintenant toutes les tables dans le fichier (en fait, vous pouvez les stocker dans des fichiers séparés si vous le souhaitez, vous devrez probablement ajouter le nom du fichier au group_map, mais ce n'est probablement pas nécessaire).

Voici comment obtenir des colonnes et en créer de nouvelles :

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)

Lorsque vous êtes prêt pour le post_traitement :

# 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)

À propos de data_columns, vous n'avez pas réellement besoin de définir ANY data_columns ; ils vous permettent de sous-sélectionner des lignes en fonction de la colonne. Par exemple. quelque chose comme :

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

Ils peuvent être plus intéressants pour vous lors de l'étape de génération du rapport final (essentiellement, une colonne de données est séparée des autres colonnes, ce qui peut affecter quelque peu l'efficacité si vous en définissez beaucoup).

Vous pouvez également :

  • créez une fonction qui prend une liste de champs, recherche les groupes dans le groups_map, puis les sélectionne et concatène les résultats afin d'obtenir le cadre résultant (c'est essentiellement ce que fait select_as_multiple). De cette façon, la structure serait assez transparente pour vous.
  • indexe sur certaines colonnes de données (rend le sous-ensemble de lignes beaucoup plus rapide).
  • activer la compression.

Faites-moi savoir si vous avez des questions !