Lors de l'analyse de données de séries chronologiques, vous lisez souvent des données à partir de fichiers csv / tsv.
Si le fichier a une capacité de quelques dizaines de Mo, vous ne vous en souciez peut-être pas autant, mais s'il s'agit d'un fichier de quelques centaines de Mo, il faut quelques secondes à quelques dizaines de secondes juste pour le lire, et cela prend de quelques secondes à quelques dizaines de secondes, ce qui se produit chaque fois que vous exécutez le code. Le temps devient stressant.
Ici, nous allons introduire une méthode qui peut exploser le processus de lecture avec un peu d'ingéniosité.
Mesurez le temps d'exécution avec le MBP à portée de main.
MacBook Pro (Retina, 13-inch, Mid 2014)
De plus, l'environnement d'exécution du code de mesure est la dernière version de la série 2.7.
$ python --version
$ Python 2.7.10
Utilisez le module pandas pour lire le fichier csv.
>>> import pandas
>>> pandas.__version__
0.18.1
Le fichier csv à lire utilise les données du taux de change dollar-yen par incréments d'une minute de 2005 à 2015 qui étaient à portée de main. Contient des données dans 5 colonnes de temps, prix d'ouverture, prix élevé, prix bas et prix de clôture x 3687221 lignes.
Le contenu est comme ça
time | open | high | low | close |
---|---|---|---|---|
2005-01-03 03:00:00 | 102.360 | 102.390 | 102.360 | 102.390 |
$ wc -c < usdjpy-1m.csv
204760049
C'est un fichier csv d'environ 204 Mo.
Utilisez la méthode «time» du module «time» standard pour mesurer le temps. J'ai préparé le code suivant comme préparation à cela.
La méthode calctime
est une méthode de mesure simple qui exécute une expression arbitraire et renvoie la différence entre l'heure de début et l'heure de fin.
calctime.py
#!/usr/bin/python
# -*- encoding: utf-8 -*-
from time import time
def calctime(func):
start = time()
r = func()
return {'value': r, 'time': time()-start}
Tout d'abord, pour mesurer le temps de référence, utilisez la méthode read_csv
pour mesurer le temps.
calctime.py
import pandas
csv = calctime(lambda: pandas.read_csv('usdjpy-1m.csv'))
print('Lire normalement_csv{}Ça a pris une seconde'.format(csv['time']))
Lire normalement_csv 7.56608009338 a pris 2 secondes
Cela prend 7,5 secondes. De plus, comme le fichier traité cette fois-ci est des données de séries chronologiques, j'analyserai également la colonne qui stocke l'heure dans le type datetime.
calctime.py
import pandas
csv = calctime(lambda: pandas.read_csv('usdjpy-1m.csv', parse_dates=['time'], index_col='time'))
print('lire en analysant la colonne de temps_csv{}Ça a pris une seconde'.format(csv['time']))
lire en analysant la colonne de temps_40 avec csv.0371601582 Cela a pris 2 secondes
Cela prend 40 secondes. Je ne peux pas parler d'attendre 40 secondes à chaque fois, alors accélérons.
Python est livré en standard avec un module appelé «pickle» qui sérialise les objets en mémoire en octets.
En tant que nom du système singulier de Pickles, il fournit une fonction pour convertir un objet à court terme en mémoire en une forme qui peut être sortie dans un fichier et l'enregistrer dans le stockage. Les légumes crus peuvent également être marinés et stockés pendant une longue période.
Le code ressemble à ceci.
Enregistrez l'objet une fois stocké dans la variable avec pandas.read_csv
comme usdjpy-1m.csv.pkl en utilisant pickle.dump
sur le stockage, et lisez la chaîne d'octets vidés en utilisant pickle.load
. Je mesure le temps.
calctime.py
import pandas
import pickle
#Chargement de csv
csv = pandas.read_csv('usdjpy-1m.csv', parse_dates=['time'], index_col='time')
#Vider les octets dans le fichier
pickle.dump(csv, open('usdjpy-1m.csv.pkl', 'w'))
#Recharger le fichier sauvegardé
pickled = calctime(lambda: pickle.load(open('usdjpy-1m.csv.pkl', 'w')))
print('Lors du chargement avec cornichon{}Ça a pris une seconde'.format(pickled['time']))
5 lors du chargement avec cornichon.65008401871 a pris 1 seconde
Il a été considérablement réduit de 40 secondes à 5,6 secondes.
Il semble que cela fonctionne car il n'est plus nécessaire d'analyser le type datetime à chaque fois.
Une fois que vous l'avez vidé avec pickle, vous pouvez l'utiliser comme un objet pandas en le lisant la prochaine fois, donc c'est facile. J'ai fait beaucoup de progrès.
Mais c'est assez long pour attendre 5,6 secondes à chaque fois. Rendons ce cornichon encore plus rapide.
Comme indiqué dans http://docs.python.jp/2/library/pickle.html#pickle.dump , Pickle peut spécifier le protocole à utiliser lors du déchargement.
Il existe des versions 0 à 2 du protocole, 0 étant la plus ancienne et la plus lente et 2 la plus récente et la plus rapide. La version 2 ne peut être utilisée que dans les environnements Python 2.3 ou supérieur et a la particularité de ne pas être rétrocompatible.
Et la valeur par défaut est la version 0. Si vous utilisez pickle.dump
sans rien spécifier, vous finissez par utiliser un protocole lent au lieu d'une compatibilité descendante.
Spécifions le protocole dans le code ci-dessus et réexécutons.
calctime.py
import pandas
import pickle
#Chargement de csv
csv = pandas.read_csv('usdjpy-1m.csv', parse_dates=['time'], index_col='time')
#Vider les octets dans un fichier à l'aide du protocole de la version 2
pickle.dump(csv, open('usdjpy-1m.csv.pkl', 'w'), protocol=2)
#Recharger le fichier sauvegardé
pickled = calctime(lambda: pickle.load(open('usdjpy-1m.csv.pkl', 'w')))
print('Avec la version 2{}Ça a pris une seconde'.format(pickled['time']))
1 avec la version 2.07643604279 Cela a pris 2 secondes
5,6 secondes → Elle a été raccourcie à environ 1 seconde. Vous pouvez maintenant lire à une vitesse agréable.
Mais je ne suis toujours pas satisfait. Attendre 1 seconde à chaque fois est stressant.
Enfin, ajoutons un autre effort pour exploser le code.
Si vous enregistrez la chaîne d'octets sauvegardée par pickle en tant que fichier sur le stockage, il faudra un certain temps pour la lire.
À l'inverse, le fait que le temps d'accès au stockage soit un goulot d'étranglement signifie qu'il doit être éliminé.
Ici, changeons la destination de sauvegarde en redis, qui enregistre les données en mémoire au lieu de stockage.
redis est une base de données de type magasin clé-valeur qui fonctionne en mémoire et a la particularité d'être extrêmement rapide car elle stocke des données en mémoire.
La méthode d'installation détaillée et l'explication sont omises ici, veuillez donc la vérifier si vous êtes intéressé.
Si l'installation réussit, redis fonctionne-t-il correctement?
$ redis-cli ping
PONG
Vous pouvez vérifier en tapant la commande.
Pour accéder à redis depuis python, installez le module redis
avec pip.
$ pip install redis
Modifions un peu le code précédent, sauvegardons le csv lu en tant que chaîne d'octets à vider et redis, puis mesurons le temps pour l'obtenir à partir de redis.
Cette fois, ni le vidage ni le chargement ne sont effectués en spécifiant un fichier, mais nous voulons l'étendre à une variable, donc les méthodes utilisées sont pickle.dumps
et pickle.loads
.
calctime.py
import pandas
import pickle
import redis
#Chargement de csv
csv = pandas.read_csv('usdjpy-1m.csv', parse_dates=['time'], index_col='time')
#Vider les octets à l'aide du protocole de la version 2
dumped = pickle.dumps(csv, protocol=2)
#Enregistrez la chaîne d'octets vidée dans redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.set('usdjpy-1m.csv', dumped)
#Lire les octets de redis et restaurer avec pickle
csv = calctime(lambda: pickle.loads(r.get('usdjpy-1m.csv')))
print('Avec redis{}Ça a pris une seconde'.format(csv['time']))
0 avec redis.Cela a pris 332698106766 secondes
Il a été réduit de 1 seconde à 0,33 seconde.
Avec cela, vous pouvez exécuter le code sans aucun stress! Par rapport aux 40 premières secondes de chargement, il était 121 fois plus rapide.
Voici l'ensemble de code utilisé dans l'expérience.
calctime.py
#!/usr/bin/python
# -*- encoding: utf-8 -*-
import pandas
import pickle
import redis
from time import time
#Méthode de mesure du temps
def calctime(func):
start = time()
r = func()
return {'value': r, 'time': time()-start}
#chemin du fichier csv
csv_filepath = 'usdjpy-1m.csv'
#Lire normalement
read_csv = calctime(lambda: pandas.read_csv(csv_filepath, parse_dates=['time'], index_col='time'))
print('Lire normalement_csv{}Ça a pris une seconde'.format(read_csv['time']))
#pickle Lire à l'aide de Protocolol version 0
pickle.dump(read_csv['value'], open('csv0.pkl', 'w'))
pickle_load_0 = calctime(lambda: pickle.load(open('csv0.pkl', 'r')))
print('pickle avec Protocolol Version 0{}Ça a pris une seconde'.format(pickle_load_0['time']))
#pickle Lire avec Protocolol version 2
pickle.dump(read_csv['value'], open('csv2.pkl', 'w'), protocol=2)
pickle_load_2 = calctime(lambda: pickle.load(open('csv2.pkl', 'r')))
print('Avec pickle Protocolol Version 2{}Ça a pris une seconde'.format(pickle_load_2['time']))
#Lire à l'aide de redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.set(csv_filepath, pickle.dumps(read_csv['value'], protocol=2))
redis_get = calctime(lambda: pickle.loads(r.get(csv_filepath)))
print('Avec redis{}Ça a pris une seconde'.format(redis_get['time']))
Lors de l'analyse des données, je pense qu'il existe de nombreuses situations dans lesquelles vous devez exécuter le code plusieurs fois et répéter des essais et des erreurs, mais j'espère que cela vous aidera à faire pivoter les itérations aussi efficacement que possible dans de tels cas.
L'auteur publie quotidiennement des informations techniques sur Twitter. Je vous serais reconnaissant si vous pouviez me suivre.
https://twitter.com/YuhsakInoue
Recommended Posts