Flux de travail «grandes données» à l'aide de pandas

982

J'ai essayé de trouver une réponse à cette question pendant de nombreux mois tout en apprenant les pandas. J'utilise SAS pour mon travail quotidien et c'est génial pour son support hors cœur. Cependant, SAS est horrible en tant que logiciel pour de nombreuses autres raisons.

Un jour, j'espère remplacer mon utilisation de SAS par python et pandas, mais il me manque actuellement un flux de travail hors cœur pour les grands ensembles de données. Je ne parle pas de "big data" qui nécessite un réseau distribué, mais plutôt de fichiers trop volumineux pour tenir en mémoire mais assez petits pour tenir sur un disque dur.

Ma première pensée est d'utiliser HDFStorepour conserver de grands ensembles de données sur le disque et extraire uniquement les éléments dont j'ai besoin dans les trames de données pour l'analyse. D'autres ont mentionné MongoDB comme une alternative plus facile à utiliser. Ma question est la suivante:

Quels sont les workflows de bonnes pratiques pour accomplir les tâches suivantes:

  1. Chargement de fichiers plats dans une structure de base de données permanente sur disque
  2. Interroger cette base de données pour récupérer des données à alimenter dans une structure de données pandas
  3. Mise à jour de la base de données après manipulation de pièces dans des pandas

Des exemples concrets seraient très appréciés, en particulier de la part de ceux qui utilisent des pandas sur des "grandes données".

Modifier - un exemple de la façon dont j'aimerais que cela fonctionne:

  1. Importez de manière itérative un grand fichier plat et stockez-le dans une structure de base de données permanente sur disque. Ces fichiers sont généralement trop volumineux pour tenir en mémoire.
  2. Afin d'utiliser Pandas, je voudrais lire des sous-ensembles de ces données (généralement seulement quelques colonnes à la fois) qui peuvent tenir en mémoire.
  3. Je créerais de nouvelles colonnes en effectuant diverses opérations sur les colonnes sélectionnées.
  4. Je devrais ensuite ajouter ces nouvelles colonnes dans la structure de la base de données.

J'essaie de trouver un moyen pratique d'exécuter ces étapes. En lisant des liens sur les pandas et les pytables, il semble que l'ajout d'une nouvelle colonne pourrait être un problème.

Modifier - Répondre spécifiquement aux questions de Jeff:

  1. Je construis des modèles de risque de crédit à la consommation. Les types de données comprennent les caractéristiques du téléphone, du SSN et de l'adresse; valeurs des propriétés; des informations désobligeantes comme les casiers judiciaires, les faillites, etc ... Les ensembles de données que j'utilise quotidiennement comportent près de 1 000 à 2 000 champs en moyenne de types de données mixtes: variables continues, nominales et ordinales de données numériques et de caractères. J'ajoute rarement des lignes, mais j'effectue de nombreuses opérations qui créent de nouvelles colonnes.
  2. Les opérations typiques impliquent la combinaison de plusieurs colonnes utilisant une logique conditionnelle dans une nouvelle colonne composée. Par exemple if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B',. Le résultat de ces opérations est une nouvelle colonne pour chaque enregistrement de mon jeu de données.
  3. Enfin, je voudrais ajouter ces nouvelles colonnes dans la structure de données sur disque. Je répéterais l'étape 2, en explorant les données avec des tableaux croisés et des statistiques descriptives en essayant de trouver des relations intéressantes et intuitives à modéliser.
  4. Un fichier de projet typique fait généralement environ 1 Go. Les fichiers sont organisés de manière à ce qu'une ligne se compose d'un enregistrement de données de consommation. Chaque ligne a le même nombre de colonnes pour chaque enregistrement. Ce sera toujours le cas.
  5. Il est assez rare que je sous-ensemble de lignes lors de la création d'une nouvelle colonne. Cependant, il est assez courant pour moi de sous-définir des lignes lors de la création de rapports ou de la génération de statistiques descriptives. Par exemple, je souhaiterais peut-être créer une fréquence simple pour un secteur d'activité spécifique, par exemple les cartes de crédit Retail. Pour ce faire, je ne sélectionnerais que les enregistrements pour lesquels le secteur d'activité = vente au détail, en plus des colonnes sur lesquelles je souhaite faire rapport. Cependant, lors de la création de nouvelles colonnes, je tirais toutes les lignes de données et uniquement les colonnes dont j'avais besoin pour les opérations.
  6. Le processus de modélisation nécessite d'analyser chaque colonne, de rechercher des relations intéressantes avec une variable de résultat et de créer de nouvelles colonnes composées qui décrivent ces relations. Les colonnes que j'explore se font généralement en petits ensembles. Par exemple, je vais me concentrer sur un ensemble de disons 20 colonnes traitant uniquement des valeurs des propriétés et observer comment elles sont liées au défaut de paiement d'un prêt. Une fois que ceux-ci sont explorés et que de nouvelles colonnes sont créées, je passe ensuite à un autre groupe de colonnes, disons l'enseignement collégial, et je répète le processus. Ce que je fais, c'est créer des variables candidates qui expliquent la relation entre mes données et certains résultats. À la toute fin de ce processus, j'applique certaines techniques d'apprentissage qui créent une équation à partir de ces colonnes composées.

Il est rare que j'ajoute des lignes à l'ensemble de données. Je créerai presque toujours de nouvelles colonnes (variables ou fonctionnalités dans le langage statistique / d'apprentissage automatique).

Zelazny7
la source
1
Le rapport taille du noyau / taille réelle est-il de 1%, 10%? Est-ce important - si vous pouviez compresser des cols en int8, ou filtrer les lignes bruyantes, cela changerait-il votre boucle de calcul-réflexion, par exemple des heures en minutes? (Ajoutez également une balise de données volumineuses.)
denis
1
Stocker float32 au lieu de float64, et int8 dans la mesure du possible, devrait être trivial (je ne sais pas quels outils / fonctions font float64 en interne cependant)
denis
pouvez-vous diviser votre tâche en morceaux de travail?
Andrew Scott Evans,
1
une bonne solution 2019 pour faire des pandas comme des opérations sur des données "moyennes" qui ne tiennent pas en mémoire est dask
lunguini
Il existe des alternatives à python + pandas que vous voudrez peut-être envisager de voir au tout début. Considérez le fait que Python est un langage de programmation à usage général (pas un DSL pour le munging et l'analyse de données) et que pandas est une bibliothèque clouée en plus de cela. J'envisagerais de regarder R ou kdb.
Henry Henrinson

Réponses:

622

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

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

Des 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 simplement des colonnes?
  2. À quoi ressembleront les opérations typiques. Par exemple, faites une requête sur les 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.
    (Donner un exemple de jouet pourrait nous permettre de proposer des recommandations plus spécifiques.)
  3. Après ce traitement, que faites-vous? L'étape 2 est-elle ad hoc ou répétable?
  4. Entrer des fichiers plats: combien, taille totale approximative en Go. Comment sont-ils organisés, par exemple, par registres? Est-ce que chacun contient des champs différents ou ont-ils des enregistrements par fichier avec tous les champs de chaque fichier?
  5. Avez-vous déjà sélectionné des sous-ensembles de lignes (enregistrements) en fonction de critères (par exemple, sélectionnez 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 n'avez pas besoin de tirer cette colonne explicite jusqu'à ce que temps des résultats finaux)?

Solution

Assurez-vous d'avoir au moins des pandas0.10.1 installés.

Lisez les fichiers itératifs morceau par morceau et les requêtes de table multiples .

Étant donné que pytables est optimisé pour fonctionner en ligne (c'est 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 (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'] ]))

Lire les fichiers et créer le stockage (essentiellement faire ce qui append_to_multiplefait):

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

Maintenant, vous avez 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 de fichier à 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)

Concernant data_columns, vous n'avez en fait pas 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 à l'étape de la génération du rapport final (essentiellement une colonne de données est séparée des autres colonnes, ce qui pourrait avoir un impact sur l'efficacité si vous en définissez beaucoup).

Vous pouvez également vouloir:

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

Faites-moi savoir quand vous avez des questions!

Jeff
la source
5
Merci pour les liens. Le deuxième lien me fait un peu peur que je ne puisse pas ajouter de nouvelles colonnes aux tables dans HDFStore? Est-ce exact? J'ai également ajouté un exemple de la façon dont j'utiliserais cette configuration.
Zelazny7
4
La structure réelle du hdf dépend de vous. Pytables est orienté ligne, avec des colonnes fixes au moment de la création. Vous ne pouvez pas ajouter de colonnes une fois la table créée. Cependant, vous pouvez créer une nouvelle table indexée de la même manière que votre table existante. (voir les exemples select_as_multiple dans la documentation). De cette façon, vous pouvez créer des objets de taille arbitraire tout en ayant des requêtes assez efficaces. La façon dont vous utilisez les données est essentielle à la façon dont elles doivent être organisées sur le disque. Envoyez-moi un e-mail hors liste avec un pseudo-code d'un exemple plus spécifique.
Jeff
1
J'ai mis à jour ma question pour répondre à vos points détaillés. Je vais travailler sur un exemple pour vous envoyer hors liste. Merci!
Zelazny7
12
@Jeff, avec les Pandas à 0.17.x, les problèmes décrits ci-dessus ont-ils été résolus dans Pandas?
ctrl-alt-delete
5
@Jeff tient à ajouter une mise à jour rapide sur votre réponse pour promouvoir Dask?
Boud
137

Je pense que les réponses ci-dessus manquent d'une approche simple que j'ai trouvée très utile.

Lorsque j'ai un fichier trop volumineux pour être chargé en mémoire, je le divise en plusieurs fichiers plus petits (soit par ligne, soit par colonnes)

Exemple: Dans le cas de 30 jours de données de trading d'une taille de ~ 30 Go, je les divise en un fichier par jour de ~ 1 Go. Je traite ensuite chaque fichier séparément et agrège les résultats à la fin

L'un des plus grands avantages est qu'il permet le traitement parallèle des fichiers (soit plusieurs threads ou processus)

L'autre avantage est que la manipulation de fichiers (comme l'ajout / la suppression de dates dans l'exemple) peut être accomplie par des commandes shell régulières, ce qui n'est pas possible dans des formats de fichiers plus avancés / compliqués

Cette approche ne couvre pas tous les scénarios, mais est très utile dans beaucoup d'entre eux

user1827356
la source
39
D'accord. Avec tout le battage médiatique, il est facile d'oublier que les outils de ligne de commande peuvent être 235x plus rapides qu'un cluster Hadoop
zelusp
84

Il y a maintenant, deux ans après la question, un équivalent de pandas «hors du cœur»: dask . C'est excellent! Bien qu'il ne prenne pas en charge toutes les fonctionnalités de pandas, vous pouvez aller très loin avec.

Privé
la source
6
et pour un exemple complet avec dask, jetez un œil ici stackoverflow.com/questions/37979167/…
ℕʘʘḆḽḘ
En fonction de vos données, il est logique de jeter un œil à pystore . Cela dépend dask.
gies0r
66

Si vos jeux de données se situent entre 1 et 20 Go, vous devriez obtenir un poste de travail avec 48 Go de RAM. Les pandas peuvent ensuite conserver l'ensemble de données en RAM. Je sais que ce n'est pas la réponse que vous cherchez ici, mais faire du calcul scientifique sur un ordinateur portable avec 4 Go de RAM n'est pas raisonnable.

rjurney
la source
7
"Faire du calcul scientifique sur un ordinateur portable avec 4 Go de RAM n'est pas raisonnable" Définissez raisonnable. Je pense qu'UNIVAC aurait un point de vue différent. arstechnica.com/tech-policy/2011/09/…
grisaitis
2
D'accord! essayez de continuer à travailler dans la mémoire même si cela coûte $ à l'avance. Si votre travail mène à un rendement financier, alors au fil du temps, vous récupérerez vos dépenses grâce à votre efficacité accrue.
ansonw
2
Faire du calcul scientifique sur un poste de travail avec 48 Go de RAM n'est pas raisonnable.
Yaroslav Nikitenko
4
@YaroslavNikitenko Un r4.2xlarge avec 61 Go / RAM coûte 0,532 $ / heure. Quel type de calcul scientifique faites-vous qui n'a pas une telle valeur? Cela semble inhabituel, sinon déraisonnable.
rjurney
4
@rjurney désolé, j'aurais peut-être dû supprimer mon commentaire. Votre jugement sur un ordinateur scientifique "déraisonnable" semble très subjectif. Je fais mes calculs scientifiques depuis des années sur des ordinateurs portables, et cela me semble suffisant, car la plupart du temps j'écris du code. Mes algorithmes sont beaucoup plus difficiles du point de vue de la programmation que du point de vue informatique. De plus, je suis presque sûr que pour écrire des algorithmes évolutifs, il ne faut pas se fier aux limitations matérielles actuelles. Votre commentaire sur l'informatique d'autres personnes peut sembler un peu offensant (mis à part la subjectivité).
Yaroslav Nikitenko
59

Je sais que c'est un vieux fil, mais je pense que la bibliothèque Blaze mérite d'être vérifiée. Il est conçu pour ces types de situations.

De la documentation:

Blaze étend l'utilisabilité de NumPy et Pandas à l'informatique distribuée et hors cœur. Blaze fournit une interface similaire à celle de NumPy ND-Array ou Pandas DataFrame, mais mappe ces interfaces familières sur une variété d'autres moteurs de calcul comme Postgres ou Spark.

Edit: Soit dit en passant, il est soutenu par ContinuumIO et Travis Oliphant, auteur de NumPy.

chishaku
la source
GraphLab Create est une autre bibliothèque qui mérite d'être étudiée: elle a une structure de type DataFrame efficace qui n'est pas limitée par la capacité de la mémoire. blog.dato.com/…
étanche
52

C'est le cas du pymongo. J'ai également prototypé en utilisant SQL Server, SQLite, HDF, ORM (SQLAlchemy) en Python. Tout d'abord, pymongo est une base de données basée sur des documents, donc chaque personne serait un document ( dictd'attributs). De nombreuses personnes forment une collection et vous pouvez avoir de nombreuses collections (personnes, bourse, revenus).

pd.dateframe -> pymongo Remarque: J'utilise l' chunksizein read_csvpour le garder à 5 à 10k enregistrements (pymongo supprime le socket s'il est plus grand)

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

interrogation: gt = supérieur à ...

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

.find() renvoie un itérateur donc j'utilise couramment ichunked pour couper en petits itérateurs.

Que diriez-vous d'une jointure puisque j'obtiens normalement 10 sources de données à coller ensemble:

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

puis (dans mon cas parfois je dois d'abord m'aggraver aJoinDFavant son "fusionnable".)

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

Et vous pouvez ensuite écrire les nouvelles informations dans votre collection principale via la méthode de mise à jour ci-dessous. (collection logique vs sources de données physiques).

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

Pour les recherches plus petites, dénormalisez simplement. Par exemple, vous avez du code dans le document et vous ajoutez simplement le texte du code de champ et effectuez une dictrecherche lorsque vous créez des documents.

Maintenant, vous avez un bel ensemble de données basé sur une personne, vous pouvez libérer votre logique sur chaque cas et créer plus d'attributs. Enfin, vous pouvez lire dans les pandas vos indicateurs clés 3 à mémoire max et faire des pivots / agg / exploration de données. Cela fonctionne pour moi pour 3 millions d'enregistrements avec des nombres / gros texte / catégories / codes / flotteurs / ...

Vous pouvez également utiliser les deux méthodes intégrées à MongoDB (MapReduce et framework d'agrégation). Voir ici pour plus d'informations sur le cadre d'agrégation , car il semble être plus facile que MapReduce et semble pratique pour un travail d'agrégation rapide. Notez que je n'avais pas besoin de définir mes champs ou relations, et je peux ajouter des éléments à un document. À l'état actuel de l'ensemble d'outils numpy, pandas et python qui évolue rapidement, MongoDB m'aide à me mettre au travail :)

brian_the_bungler
la source
Salut, je suis jouer avec votre exemple aussi bien et je cours dans cette erreur lorsque vous essayez d'insérer dans une base de données: In [96]: test.insert((a[1].to_dict() for a in df.iterrows())) --------------- InvalidDocument: Cannot encode object: 0. Des idées qui pourraient être erronées? Mon dataframe se compose de tous les dtypes int64 et est très simple.
Zelazny7
2
Oui, j'ai fait la même chose pour une gamme DF simple et l'int64 de numpy semble déranger pymongo. Toutes les données que j'ai joué avec convertissent de CSV (vs artificiellement via range ()) et ont des types longs et donc aucun problème. En numpy, vous pouvez convertir, mais je vois cela comme néfaste. Je dois admettre que les articles 10.1 pour HDF sont passionnants.
brian_the_bungler
43

Je l'ai repéré un peu tard, mais je travaille avec un problème similaire (modèles de remboursement anticipé hypothécaire). Ma solution a été de sauter la couche HDFStore des pandas et d'utiliser des pytables droits. J'enregistre chaque colonne en tant que matrice HDF5 individuelle dans mon fichier final.

Mon flux de travail de base consiste à obtenir d'abord un fichier CSV à partir de la base de données. Je le gzip, donc ce n'est pas aussi énorme. Ensuite, je convertis cela en un fichier HDF5 orienté lignes, en itérant dessus en python, en convertissant chaque ligne en un vrai type de données et en l'écrivant dans un fichier HDF5. Cela prend quelques dizaines de minutes, mais il n'utilise pas de mémoire, car il ne fonctionne que ligne par ligne. Ensuite, je «transpose» le fichier HDF5 orienté lignes en un fichier HDF5 orienté colonnes.

Le tableau transpose ressemble à:

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

La relire ressemble alors à:

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)

Maintenant, je l'exécute généralement sur une machine avec une tonne de mémoire, donc je ne fais peut-être pas assez attention à mon utilisation de la mémoire. Par exemple, par défaut, l'opération de chargement lit l'ensemble des données.

Cela fonctionne généralement pour moi, mais c'est un peu maladroit, et je ne peux pas utiliser la magie des pytables fantaisistes.

Edit: Le véritable avantage de cette approche, par rapport au tableau des enregistrements pytables par défaut, est que je peux ensuite charger les données dans R en utilisant h5r, qui ne peut pas gérer les tables. Ou, au moins, je n'ai pas pu l'obtenir pour charger des tables hétérogènes.

Johann Hibschman
la source
Pourriez-vous partager avec moi une partie de votre code? Je suis intéressé par la façon dont vous chargez les données à partir d'un format de texte plat sans connaître les types de données avant de passer aux pytables. En outre, il semble que vous ne travaillez qu'avec des données d'un seul type. Est-ce exact?
Zelazny7
1
Tout d'abord, je suppose que je connais les types de colonnes avant le chargement, plutôt que d'essayer de deviner à partir des données. J'enregistre un fichier "data spec" JSON avec les noms et types de colonnes et je l'utilise lors du traitement des données. (Le fichier est généralement une sortie BCP horrible sans aucune étiquette.) Les types de données que j'utilise sont des chaînes, des flottants, des entiers ou des dates mensuelles. Je transforme les chaînes en entiers en enregistrant une table d'énumération et en convertissant les dates en entiers (mois après 2000), donc je n'ai plus que des ints et des flottants dans mes données, plus l'énumération. J'enregistre les flotteurs en tant que float64 maintenant, mais j'ai expérimenté avec float32.
Johann Hibschman
1
si vous avez le temps, essayez ceci pour une compatibilité externe avec R: pandas.pydata.org/pandas-docs/dev/… , et si vous avez des difficultés, nous pouvons peut-être le modifier
Jeff
J'essaierai, si je peux. rhdf5 est une douleur, car il s'agit d'un package bioconducteur, plutôt que d'être simplement sur CRAN comme h5r. Je suis à la merci de notre équipe d'architecture technique, et il y a eu un problème avec rhdf5 la dernière fois que je l'ai demandé. Dans tous les cas, il semble juste qu'une erreur soit orientée ligne plutôt que colonne avec un magasin OLAP, mais maintenant je divague.
Johann Hibschman
39

Une astuce que j'ai trouvée utile pour les cas d'utilisation de données volumineuses consiste à réduire le volume des données en réduisant la précision du flottant à 32 bits. Ce n'est pas applicable dans tous les cas, mais dans de nombreuses applications, la précision 64 bits est excessive et les économies de mémoire 2x en valent la peine. Pour rendre un point évident encore plus évident:

>>> 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
la source
27

Comme indiqué par d'autres, après quelques années, un équivalent de pandas `` hors du cœur '' a émergé: dask . Bien que dask ne remplace pas les pandas et toutes ses fonctionnalités, il se démarque pour plusieurs raisons:

Dask est une bibliothèque informatique parallèle flexible pour l'informatique analytique qui est optimisée pour la planification dynamique des tâches pour les charges de travail de calcul interactives des collections de «Big Data» comme les tableaux parallèles, les trames de données et les listes qui étendent les interfaces communes comme NumPy, Pandas ou Python aux plus grands itérateurs. que la mémoire ou les environnements distribués et évolue des ordinateurs portables aux clusters.

Dask met l'accent sur les vertus suivantes:

  • Familier: fournit un tableau NumPy parallélisé et des objets Pandas DataFrame
  • Flexible: fournit une interface de planification des tâches pour des charges de travail plus personnalisées et une intégration avec d'autres projets.
  • Natif: permet l'informatique distribuée en Pure Python avec accès à la pile PyData.
  • Rapide: fonctionne avec une faible surcharge, une faible latence et une sérialisation minimale nécessaire pour des algorithmes numériques rapides
  • Évolutivité: fonctionne de manière résiliente sur des clusters avec des milliers de cœurs Évolutivité: pratique à configurer et à exécuter sur un ordinateur portable en un seul processus
  • Réactif: conçu en tenant compte de l'informatique interactive, il fournit des informations et des diagnostics rapides pour aider les humains

et pour ajouter un exemple de code simple:

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

remplace un code pandas comme celui-ci:

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

et, particulièrement remarquable, fournit à travers l' concurrent.futuresinterface une infrastructure générale pour la soumission de tâches personnalisées:

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
la source
J'ai ajouté cette réponse car le message de @Private apparaît régulièrement sur la liste de suppression suggérée pour le contenu et la longueur.
wp78de
17

Il convient également de mentionner ici Ray ,
c'est un cadre de calcul distribué, qui a sa propre implémentation pour les pandas de manière distribuée.

Remplacez simplement l'importation des pandas et le code devrait fonctionner tel quel:

# import pandas as pd
import ray.dataframe as pd

#use pd as usual

pouvez lire plus de détails ici:

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

lev
la source
16

Encore une variation

De nombreuses opérations effectuées dans les pandas peuvent également être effectuées sous forme de requête db (sql, mongo)

L'utilisation d'un SGBDR ou d'un mongodb vous permet d'effectuer certaines des agrégations dans la requête DB (qui est optimisée pour les données volumineuses et utilise efficacement le cache et les index)

Plus tard, vous pouvez effectuer un post-traitement à l'aide de pandas.

L'avantage de cette méthode est que vous obtenez les optimisations de base de données pour travailler avec des données volumineuses, tout en définissant la logique dans une syntaxe déclarative de haut niveau - et sans avoir à vous occuper des détails de décider quoi faire en mémoire et que faire du noyau.

Et bien que le langage de requête et les pandas soient différents, il n'est généralement pas compliqué de traduire une partie de la logique de l'un à l'autre.

Ophir Yoktan
la source
11

Considérez Ruffus si vous choisissez le chemin simple de création d'un pipeline de données divisé en plusieurs fichiers plus petits.

Golf Monkey
la source
9

J'ai récemment rencontré un problème similaire. J'ai trouvé que lire simplement les données en morceaux et les ajouter pendant que je les écris en morceaux sur le même csv fonctionne bien. Mon problème était l'ajout d'une colonne de date basée sur les informations d'une autre table, en utilisant la valeur de certaines colonnes comme suit. Cela peut aider ceux qui sont confus par dask et hdf5 mais plus familiers avec des pandas comme moi.

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
la source
9

Je voudrais souligner le package Vaex.

Vaex est une bibliothèque python pour les DataFrames Out-of-Core paresseux (similaires aux Pandas), pour visualiser et explorer de grands ensembles de données tabulaires. Il peut calculer des statistiques telles que la moyenne, la somme, le nombre, l'écart-type, etc., sur une grille à N dimensions jusqu'à un milliard (10 9 ) objets / lignes par seconde. La visualisation se fait à l'aide d'histogrammes, de diagrammes de densité et d'un rendu de volume 3D, permettant une exploration interactive des mégadonnées. Vaex utilise un mappage de mémoire, une politique de copie de mémoire nulle et des calculs paresseux pour de meilleures performances (pas de perte de mémoire).

Jetez un œil à la documentation: https://vaex.readthedocs.io/en/latest/ L'API est très proche de l'API des pandas.

Rob
la source
0

Pourquoi les pandas? Avez-vous essayé Standard Python ?

L'utilisation de la bibliothèque standard python. Les pandas sont soumis à des mises à jour fréquentes, même avec la sortie récente de la version stable.

En utilisant la bibliothèque Python standard, votre code s'exécutera toujours.

Une façon de le faire est d'avoir une idée de la façon dont vous souhaitez que vos données soient stockées et des questions que vous souhaitez résoudre concernant les données. Ensuite, dessinez un schéma de la façon dont vous pouvez organiser vos données (tableaux de réflexion) qui vous aidera à interroger les données, pas nécessairement la normalisation.

Vous pouvez faire bon usage de:

  • liste des dictionnaires pour stocker les données en mémoire, un dict étant une ligne,
  • des générateurs pour traiter les données ligne après ligne afin de ne pas déborder votre RAM,
  • liste de compréhension pour interroger vos données,
  • utiliser Counter, DefaultDict, ...
  • stocker vos données sur votre disque dur en utilisant la solution de stockage que vous avez choisie, json pourrait être l'un d'entre eux.

Ram et HDD devient de moins en moins cher avec le temps et le python 3 standard est largement disponible et stable.

Pélican
la source
-1

En ce moment, je travaille "comme" vous, juste à une échelle inférieure, c'est pourquoi je n'ai pas de PoC pour ma suggestion.

Cependant, il semble que je réussisse à utiliser le cornichon comme système de mise en cache et à externaliser l'exécution de diverses fonctions dans des fichiers - en exécutant ces fichiers à partir de mon fichier commando / principal; Par exemple, j'utilise un prepare_use.py pour convertir les types d'objets, diviser un ensemble de données en ensemble de données de test, de validation et de prédiction.

Comment fonctionne votre mise en cache avec des cornichons? J'utilise des chaînes afin d'accéder aux fichiers de pickle qui sont créés dynamiquement, en fonction des paramètres et des ensembles de données qui ont été passés (avec cela j'essaie de capturer et de déterminer si le programme a déjà été exécuté, en utilisant .shape pour l'ensemble de données, dict pour passé paramètres). En respectant ces mesures, j'obtiens une chaîne pour essayer de trouver et de lire un fichier .pickle et peut, s'il est trouvé, sauter le temps de traitement afin de passer à l'exécution sur laquelle je travaille en ce moment.

En utilisant des bases de données, j'ai rencontré des problèmes similaires, c'est pourquoi j'ai trouvé la joie d'utiliser cette solution, cependant - il y a certainement de nombreuses contraintes - par exemple, stocker d'énormes jeux de cornichons en raison de la redondance. La mise à jour d'une table d'avant en après une transformation peut se faire avec une indexation appropriée - la validation des informations ouvre un tout autre livre (j'ai essayé de consolider les données de loyer analysées et j'ai cessé d'utiliser une base de données après 2 heures essentiellement - comme j'aurais aimé revenir en arrière après chaque processus de transformation)

J'espère que mes 2 cents vous aideront d'une manière ou d'une autre.

Salutations.

TiRoX
la source