Comment gérer de grands ensembles de données dans les pandas. Nous travaillons avec la base de données FIAS en utilisant python et 8 Go de mémoire

Il n'est pas nécessaire de représenter spécifiquement la base FIAS:



Vous pouvez le tĂ©lĂ©charger en cliquant sur le lien , cette base de donnĂ©es est ouverte et contient toutes les adresses des objets en Russie (registre d'adresses). L'intĂ©rĂȘt pour cette base de donnĂ©es est dĂ» au fait que les fichiers qu'elle contient sont assez volumineux. Ainsi, par exemple, le plus petit est de 2,9 Go. Il est proposĂ© de s'y arrĂȘter et de voir si les pandas peuvent y faire face si vous travaillez sur une machine avec seulement 8 Go de RAM. Et si vous ne pouvez pas faire face, quelles sont les options pour alimenter les pandas dans ce fichier.

La main sur le cƓur, je n'ai jamais rencontrĂ© cette base et c'est un obstacle supplĂ©mentaire, car le format des donnĂ©es qui y sont prĂ©sentĂ©es n'est pas du tout clair.

AprÚs avoir téléchargé l'archive fias_xml.rar avec la base, nous en obtenons le fichier - AS_ADDROBJ_20190915_9b13b2a6-b3bd-4866-bd1c-7ab966fafcf0.XML. Le fichier est au format xml.

Pour un travail plus pratique dans les pandas, il est recommandé de convertir xml en csv ou json.
Cependant, toutes les tentatives de conversion de programmes tiers et de python lui-mĂȘme entraĂźnent une erreur ou un arrĂȘt "MemoryError".

Hm. Et si je coupe le fichier et le convertis en plusieurs parties? C’est une bonne idĂ©e, mais tous les "cutters" essaient Ă©galement de lire l’ensemble du fichier en mĂ©moire et de le bloquer, le python lui-mĂȘme, qui suit le chemin des "cutters", ne le coupe pas. 8 Go ne suffisent Ă©videmment pas? Voyons voir.

Programme Vedit


Vous devrez utiliser un programme vedit tiers.

Ce programme vous permet de lire un fichier xml de 2,9 Go et de travailler avec lui.
Il vous permet Ă©galement de le diviser. Mais il y a un petit truc.

Comme vous pouvez le voir lors de la lecture d'un fichier, celui-ci a, entre autres, une balise AddressObjects d'ouverture:



Ainsi, en créant des parties de ce gros fichier, vous ne devez pas oublier de le fermer (tag).

Autrement dit, le début de chaque fichier xml sera comme ceci:

<?xml version="1.0" encoding="utf-8"?><AddressObjects> 

et se terminant:

 </AddressObjects> 

Coupez maintenant la premiĂšre partie du fichier (pour les autres parties, les Ă©tapes sont les mĂȘmes).

Dans le programme vedit:



Ensuite, sĂ©lectionnez Goto et Line #. Dans la fenĂȘtre qui s'ouvre, Ă©crivez le numĂ©ro de ligne, par exemple 1 000 000:



Ensuite, vous devez ajuster le bloc sélectionné afin qu'il capture à la fin l'objet dans la base de données avant la balise de fermeture:



Ce n'est pas grave s'il y a un léger chevauchement sur l'objet suivant.

Ensuite, dans le programme vedit, enregistrez le fragment sélectionné - Fichier, Enregistrer sous.

De la mĂȘme maniĂšre, nous crĂ©ons les parties restantes du fichier, marquant le dĂ©but du bloc de sĂ©lection et la fin par incrĂ©ments de 1 million de lignes.

Par conséquent, vous devriez obtenir le 4e fichier xml d'une taille d'environ 610 Mo.

Nous finalisons les parties xml


Vous devez maintenant ajouter des balises dans les fichiers xml nouvellement créés afin qu'ils se lisent au format xml.

Ouvrez les fichiers dans vedit un par un et ajoutez au début de chaque fichier:

 <?xml version="1.0" encoding="utf-8"?><AddressObjects> 

et Ă  la fin:

 </AddressObjects> 

Ainsi, nous avons maintenant 4 parties xml du fichier source divisé.

Xml-Ă -csv


Maintenant, traduisez xml en csv en Ă©crivant un programme python.

Code de programme

ici
 # -*- coding: utf-8 -*- from __future__ import unicode_literals import codecs,os import xml.etree.ElementTree as ET import csv from datetime import datetime parser = ET.XMLParser(encoding="utf-8") tree = ET.parse("add-30-40.xml",parser=parser) root = tree.getroot() Resident_data = open('fias-30-40.csv', 'w',encoding='UTF8') csvwriter = csv.writer(Resident_data) start = datetime.now() for member in root.findall('Object'): object = [] object.append(member.attrib['AOID']) object.append(member.attrib['AOGUID']) try: object.append(member.attrib['PARENTGUID']) except: object.append(None) try: object.append(member.attrib['PREVID']) except: object.append(None) #try: # object.append(member.attrib['NEXTID']) #except: # object.append(None) object.append(member.attrib['FORMALNAME']) object.append(member.attrib['OFFNAME']) object.append(member.attrib['SHORTNAME']) object.append(member.attrib['AOLEVEL']) object.append(member.attrib['REGIONCODE']) object.append(member.attrib['AREACODE']) object.append(member.attrib['AUTOCODE']) object.append(member.attrib['CITYCODE']) object.append(member.attrib['CTARCODE']) object.append(member.attrib['PLACECODE']) object.append(member.attrib['STREETCODE']) object.append(member.attrib['EXTRCODE']) object.append(member.attrib['SEXTCODE']) try: object.append(member.attrib['PLAINCODE']) except: object.append(None) try: object.append(member.attrib['CODE']) except: object.append(None) object.append(member.attrib['CURRSTATUS']) object.append(member.attrib['ACTSTATUS']) object.append(member.attrib['LIVESTATUS']) object.append(member.attrib['CENTSTATUS']) object.append(member.attrib['OPERSTATUS']) try: object.append(member.attrib['IFNSFL']) except: object.append(None) try: object.append(member.attrib['IFNSUL']) except: object.append(None) try: object.append(member.attrib['OKATO']) except: object.append(None) try: object.append(member.attrib['OKTMO']) except: object.append(None) try: object.append(member.attrib['POSTALCODE']) except: object.append(None) #print(len(object)) csvwriter.writerow(object) Resident_data.close() print(datetime.now()- start) #0:00:21.122437 

.
En utilisant le programme, vous devez convertir les 4 fichiers en csv.
La taille du fichier diminuera, chacune sera de 236 Mo (contre 610 Mo en xml).
En principe, vous pouvez désormais déjà travailler avec eux, via excel ou notepad ++.
Cependant, les fichiers sont toujours 4e au lieu d'un, et nous n'avons pas atteint l'objectif - traiter le fichier dans les pandas.

Coller des fichiers en un


Sous Windows, cela peut s'avĂ©rer ĂȘtre une tĂąche difficile, nous allons donc utiliser l'utilitaire de console en python appelĂ© csvkit. InstallĂ© en tant que module python:

 pip install csvkit 

* En fait, il s'agit d'un ensemble complet d'utilitaires, mais un sera nécessaire à partir de là.

AprĂšs avoir entrĂ© le dossier avec les fichiers Ă  coller dans la console, nous effectuerons le collage en un seul fichier. Étant donnĂ© que tous les fichiers sont sans en-tĂȘtes, nous attribuerons les noms de colonnes standard lors du collage: a, b, c, etc.:

 csvstack -H fias-0-10.csv fias-10-20.csv fias-20-30.csv fias-30-40.csv > joined2.csv 

La sortie est un fichier csv terminé.

Travaillons dans les pandas pour optimiser l'utilisation de la mémoire


Si vous téléchargez immédiatement le fichier sur pandas

 import pandas as pd import numpy as np gl = pd.read_csv('joined2.csv',encoding='ANSI',index_col='a') print (gl.info(memory_usage='deep')) #   def mem_usage(pandas_obj): if isinstance(pandas_obj,pd.DataFrame): usage_b = pandas_obj.memory_usage(deep=True).sum() else: # ,     ,   usage_b = pandas_obj.memory_usage(deep=True) usage_mb = usage_b / 1024 ** 2 #     return "{:03.2f} " .format(usage_mb) 

et vérifiez combien de mémoire cela prendra, le résultat peut désagréablement surprendre:



3 Go! Et cela malgré le fait que lors de la lecture des données, la premiÚre colonne «est passée» en tant que colonne d'index *, et donc le volume serait encore plus important.
* Par défaut, pandas définit son propre index de colonne.

Nous effectuerons l'optimisation en utilisant les méthodes du post et de l' article précédents:
- objet dans la catégorie;
- int64 dans uint8;
- float64 dans float32.

Pour ce faire, lors de la lecture du fichier, ajoutez des dtypes et la lecture des colonnes dans le code ressemblera Ă  ceci:

 gl = pd.read_csv('joined2.csv',encoding='ANSI',index_col='a', dtype ={ 'b':'category', 'c':'category','d':'category','e':'category', 'f':'category','g':'category', 'h':'uint8','i':'uint8','j':'uint8', 'k':'uint8','l':'uint8','m':'uint8','n':'uint16', 'o':'uint8','p':'uint8','q':'uint8','t':'uint8', 'u':'uint8','v':'uint8','w':'uint8','x':'uint8', 'r':'float32','s':'float32', 'y':'float32','z':'float32','aa':'float32','bb':'float32', 'cc':'float32' }) 

Maintenant, en ouvrant le fichier pandas, l'utilisation de la mémoire serait judicieuse:



Il reste à ajouter au fichier csv, si vous le souhaitez, les noms des colonnes réelles pour que les données aient un sens:

 AOID,AOGUID,PARENTGUID,PREVID,FORMALNAME,OFFNAME,SHORTNAME,AOLEVEL,REGIONCODE,AREACODE,AUTOCODE,CITYCODE,CTARCODE,PLACECODE,STREETCODE,EXTRCODE,SEXTCODE,PLAINCODE,CODE,CURRSTATUS,ACTSTATUS,LIVESTATUS,CENTSTATUS,OPERSTATUS,IFNSFL,IFNSUL,OKATO,OKTMO,POSTALCODE 

* Vous pouvez remplacer les noms de colonne par cette ligne, mais vous devez ensuite changer le code.
Enregistrez les premiĂšres lignes du fichier des pandas

 gl.head().to_csv('out.csv', encoding='ANSI',index_label='a') 

et voyez ce qui s'est passé dans Excel:



Code de programme pour une ouverture optimisée d'un fichier csv avec une base de données:

code
 import os import time import pandas as pd import numpy as np #     :  object-category, float64-float32, int64-int gl = pd.read_csv('joined2.csv',encoding='ANSI',index_col='a', dtype ={ 'b':'category', 'c':'category','d':'category','e':'category', 'f':'category','g':'category', 'h':'uint8','i':'uint8','j':'uint8', 'k':'uint8','l':'uint8','m':'uint8','n':'uint16', 'o':'uint8','p':'uint8','q':'uint8','t':'uint8', 'u':'uint8','v':'uint8','w':'uint8','x':'uint8', 'r':'float32','s':'float32', 'y':'float32','z':'float32','aa':'float32','bb':'float32', 'cc':'float32' }) pd.set_option('display.notebook_repr_html', False) pd.set_option('display.max_columns', 8) pd.set_option('display.max_rows', 10) pd.set_option('display.width', 80) #print (gl.head()) print (gl.info(memory_usage='deep')) #   def mem_usage(pandas_obj): if isinstance(pandas_obj,pd.DataFrame): usage_b = pandas_obj.memory_usage(deep=True).sum() else: # ,     ,   usage_b = pandas_obj.memory_usage(deep=True) usage_mb = usage_b / 1024 ** 2 #     return "{:03.2f} " .format(usage_mb) 


En conclusion, voyons la taille de l'ensemble de données:

 gl.shape 

 (3348644, 28) 

3,3 millions de lignes, 28 colonnes.

Conclusion: avec la taille initiale du fichier csv de 890 Mo, «optimisé» pour travailler avec des pandas, il occupe 1,2 Go de mémoire.
Ainsi, avec un calcul approximatif, on peut supposer qu'un fichier de 7,69 Go peut ĂȘtre ouvert dans les pandas, aprĂšs l'avoir "optimisĂ©".

Source: https://habr.com/ru/post/fr468637/


All Articles