106 votes

Graphique interactif contenant environ 20 millions de points d'échantillonnage et des gigaoctets de données

J'ai un problème (avec ma RAM) ici : elle n'est pas capable de contenir les données que je veux tracer. J'ai suffisamment d'espace HD. Y a-t-il une solution pour éviter cette "ombre" de mon jeu de données ?

Concrètement, je travaille sur le traitement numérique du signal et je dois utiliser un taux d'échantillonnage élevé. Mon framework (GNU Radio) enregistre les valeurs (pour éviter d'utiliser trop d'espace disque) en binaire. Je le décompresse. Ensuite, j'ai besoin de les tracer. J'ai besoin que le tracé soit zoomable et interactif. Et c'est un problème.

Y a-t-il un potentiel d'optimisation à cela, ou un autre logiciel/langage de programmation (comme R ou autre) qui peut gérer des ensembles de données plus importants ? En fait, je veux beaucoup plus de données dans mes tracés. Mais je n'ai pas d'expérience avec d'autres logiciels. Gnuplot échoue, avec une approche similaire à la suivante. Je ne connais pas R (encore).

import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import struct

"""
trace un fichier c

fichier c - valeurs en virgule flottante (4 octets), paires IQ, binaire
txt - index, en phase, en quadrature en texte brut

note : tracer directement avec numpy donne des fonctions ombrées
"""

# décompactage de l'ensemble de données du fichier c
def unpack_set(input_filename, output_filename):
    index = 0   # index des échantillons
    output_filename = open(output_filename, 'wb')

    with open(input_filename, "rb") as f:

        byte = f.read(4)    # lire la 1ère colonne du vecteur

        while byte != "":
        # Valeurs stockées sur Bit
            floati = struct.unpack('f', byte)   # écrire la valeur de la 1ère colonne dans une variable
            byte = f.read(4)            # lire la 2ème colonne du vecteur
            floatq = struct.unpack('f', byte)   # écrire la valeur de la 2ème colonne dans une variable
            byte = f.read(4)            # ligne suivante du vecteur et lire la 1ère colonne
            # format délimiteur pour matplotlib 
            lines = ["%d," % index, format(floati), ",",  format(floatq), "\n"]
            output_filename.writelines(lines)
            index = index + 1
    output_filename.close
    return output_filename.name

# reformate la sortie (configuration de précision ici)
def format(value):
    return "%.8f" % value            

# démarrage
def main():

    # spécifier le chemin
    fichier_decompacte = unpack_set("test01.cfile", "test01.txt")
    # passer la référence du fichier à matplotlib
    nom_fichier = str(fichier_decompacte)
    plt.plotfile(nom_fichier, cols=(0,1)) # index vs. en phase

    # facultatif
    # plt.axes([0, 0.5, 0, 100000]) # pour 100k échantillons
    plt.grid(True)
    plt.title("Diagramme du signal")
    plt.xlabel("Échantillon")
    plt.ylabel("En Phase")

    plt.show();

if __name__ == "__main__":
    main()

Quelque chose comme plt.swap_on_disk() pourrait mettre en cache les données sur mon SSD ;)

104voto

Jonathan Dursi Points 25143

Donc vos données ne sont pas si volumineuses, et le fait que vous ayez du mal à les tracer indique des problèmes avec les outils. Matplotlib offre de nombreuses options et la sortie est correcte, mais c'est un énorme consommateur de mémoire et il suppose fondamentalement que vos données sont petites. Mais il existe d'autres options.

Par exemple, j'ai généré un fichier de 20 millions de points de données 'bigdata.bin' en utilisant le script suivant :

#!/usr/bin/env python
import numpy
import scipy.io.numpyio

npts=20000000
filename='bigdata.bin'

def main():
    data = (numpy.random.uniform(0,1,(npts,3))).astype(numpy.float32)
    data[:,2] = 0.1*data[:,2]+numpy.exp(-((data[:,1]-0.5)**2.)/(0.25**2))
    fd = open(filename,'wb')
    scipy.io.numpyio.fwrite(fd,data.size,data)
    fd.close()

if __name__ == "__main__":
    main()

Cela génère un fichier d'une taille d'environ ~229 Mo, ce qui n'est pas si grand ; mais vous avez exprimé votre souhait d'aller jusqu'à des fichiers encore plus gros, donc vous allez éventuellement atteindre les limites de la mémoire.

Concentrons-nous d'abord sur les tracés non interactifs. La première chose à réaliser est que les tracés vectoriels avec des glyphes à chaque point seront un désastre - pour chacun des 20 M de points, dont la plupart vont se chevaucher de toute façon, essayer de rendre de petits croix ou cercles ou quelque chose d'autre sera un désastre, générant d'énormes fichiers et prenant beaucoup de temps. Je pense que c'est ce qui cause le dysfonctionnement de matplotlib par défaut.

Gnuplot n'a aucun problème avec cela :

gnuplot> set term png
gnuplot> set output 'foo.png'
gnuplot> plot 'bigdata.bin' binary format="%3float32" using 2:3 with dots

gnuplot

Et même Matplotlib peut être amené à fonctionner avec un peu de prudence (en choisissant un backend raster, et en utilisant des pixels pour marquer les points) :

#!/usr/bin/env python
import numpy
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

datatype=[('index',numpy.float32), ('floati',numpy.float32), 
        ('floatq',numpy.float32)]
filename='bigdata.bin'

def main():
    data = numpy.memmap(filename, datatype, 'r') 
    plt.plot(data['floati'],data['floatq'],'r,')
    plt.grid(True)
    plt.title("Signal-Diagram")
    plt.xlabel("Sample")
    plt.ylabel("In-Phase")
    plt.savefig('foo2.png')

if __name__ == "__main__":
    main()  

matplotlib

Maintenant, si vous voulez de l'interactivité, vous devrez grouper les données à tracer et zoomer en temps réel. Je ne connais pas d'outils python qui vous aideront à faire cela immédiatement.

D'un autre côté, le tracé de gros données est une tâche assez commune, et il existe des outils adaptés au travail. Paraview est mon favori personnel, et VisIt en est un autre. Ils sont principalement destinés aux données 3D, mais Paraview en particulier prend également en charge le 2D, est très interactif (et possède même une interface de script Python). Le seul défi sera d'écrire les données dans un format de fichier que Paraview peut facilement lire.

17voto

EOL Points 24342

Un projet plus récent présente un fort potentiel pour les grands ensembles de données : Bokeh, qui a été créé en ayant exactement cela à l'esprit.

En fait, seules les données pertinentes à l'échelle du graphique sont envoyées au backend d'affichage. Cette approche est beaucoup plus rapide que l'approche Matplotlib.

14voto

EOL Points 24342

Vous pouvez certainement optimiser la lecture de votre fichier : vous pourriez le lire directement dans un tableau NumPy, afin de profiter de la vitesse brute de NumPy. Vous avez quelques options. Si la RAM pose problème, vous pouvez utiliser memmap, qui garde la plupart du fichier sur le disque (au lieu de la RAM) :

# Chaque donnée est une séquence de trois flottants codés sur 32 bits :
data = np.memmap(filename, mode='r', dtype=[('index', 'float32'), ('floati','float32'), ('floatq', 'float32')])

Si la RAM n'est pas un problème, vous pouvez mettre tout le tableau en RAM avec fromfile :

data = np.fromfile(filename, dtype=[('index', 'float32'), ('floati','float32'), ('floatq', 'float32')])

Le tracé peut ensuite être effectué avec la fonction habituelle plot(*data) de Matplotlib, éventuellement en utilisant la méthode de "zoom in" proposée dans une autre solution.

8voto

Cicada Points 19550

Je suggérerais quelque chose un peu complexe mais qui devrait fonctionner : construisez votre graphique à différentes résolutions, pour différentes plages.

Pensez à Google Earth, par exemple. Si vous dézoomez au niveau maximum pour couvrir toute la planète, la résolution est la plus basse. Lorsque vous zoomez, les images changent pour des images plus détaillées, mais seulement dans la région sur laquelle vous zoomez.

Donc, essentiellement pour votre graphique (est-ce en 2D ? 3D ? Je suppose que c'est en 2D), je vous suggère de construire un grand graphique qui couvre toute la plage [0, n] avec une faible résolution, 2 graphiques plus petits qui couvrent [0, n/2] et [n/2 + 1, n] avec deux fois la résolution du grand, 4 graphiques plus petits qui couvrent [0, n/4] ... [3 * n / 4 + 1, n] avec deux fois la résolution des deux précédents, et ainsi de suite.

Je ne suis pas sûr si mon explication est vraiment claire. De plus, je ne sais pas si ce genre de graphique multi-résolution est géré par un quelconque programme de traçage existant.

2voto

nielsbot Points 9551

Je me demande s'il y a un avantage à accélérer la recherche de vos points? (Je suis intrigué par les arbres R * (r star) depuis un moment.)

Je me demande si l'utilisation d'un arbre r * dans ce cas pourrait être la solution. (lorsque vous zoomez, les noeuds les plus élevés de l'arbre pourraient contenir des informations sur le rendu plus grossier et zoomé, les noeuds plus proches des feuilles contiennent les échantillons individuels)

peut-être même mémoriser l'arbre dans la mémoire (ou toute autre structure que vous finissez par utiliser) pour maintenir vos performances et votre utilisation de la RAM basse. (vous déchargez la gestion de la mémoire sur le noyau)

j'espère que cela a du sens .. je m'égare un peu. il est tard!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X