J'essaie de faire un tracé de densité 2D (à partir de certaines données de simulation) avec matplotlib. Mes données x et y sont définies comme le log10 de certaines quantités. Comment obtenir des axes logarithmiques (avec des graduations logarithmiques mineures)?

Voici un exemple de mon code:

import numpy as np
import matplotlib.pyplot as plt

Data = np.genfromtxt("data") # A 2-column data file
x = np.log10(Data[:,0])
y = np.log10(Data[:,1])

xmin = x.min()
xmax = x.max()
ymin = y.min()
ymax = y.max()

fig = plt.figure()
ax = fig.add_subplot(111)

hist = ax.hexbin(x,y,bins='log', gridsize=(30,30), cmap=cm.Reds)
ax.axis([xmin, xmax, ymin, ymax])

plt.savefig('plot.pdf')
2
ZoibergPatient 7 oct. 2011 à 17:38

3 réponses

Meilleure réponse

Merci beaucoup pour vos suggestions.

Ci-dessous, je rejoins ma propre solution. Ce n'est guère "un exemple de travail minimum", mais j'ai déjà beaucoup retiré mon script!

En un mot, j'ai utilisé imshow pour tracer "l'image" (un histogramme 2D avec des log bins) et j'ai supprimé les axes. Ensuite, je dessine un deuxième tracé vide (et transparent), exactement au-dessus du premier tracé juste pour obtenir des axes de journalisation car imshow ne semble pas le permettre. Assez compliqué si vous me demandez!

Mon code est probablement loin d'être optimal car je suis nouveau sur python et matplotlib ...

Soit dit en passant, je n'utilise pas hexbin pour deux raisons: 1) Il est trop lent pour s'exécuter sur de très gros fichiers de données comme le mien. 2) Avec la version que j'utilise, les hexagones sont légèrement trop grands, c'est-à-dire qu'ils se chevauchent, résultant en des "pixels" de formes et de tailles irrégulières. De plus, je veux pouvoir écrire les données de l'histogramme dans un fichier au format texte.

#!/usr/bin/python

# How to get log axis with a 2D colormap (i.e. an "image") ??
#############################################################
#############################################################

import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import math

# Data file containing 2D data in log-log coordinates.
# The format of the file is 3 columns : x y v
# where v is the value to plotted for coordinate (x,y)
# x and y are already log values
# For instance, this can be a 2D histogram with log bins.
input_file="histo2d.dat"

# Parameters to set space for the plot ("bounding box")
x1_bb, y1_bb, x2_bb, y2_bb = 0.125, 0.12, 0.8, 0.925

# Parameters to set space for colorbar
cb_fraction=0.15
cb_pad=0.05

# Return unique values from a sorted list, will be required later
def uniq(seq, idfun=None): 
    # order preserving
    if idfun is None:
        def idfun(x): return x
    seen = {}
    result = []
    for item in seq:
        marker = idfun(item)
        # in old Python versions:
        # if seen.has_key(marker)
        # but in new ones:
        if marker in seen: continue
        seen[marker] = 1
        result.append(item)
    return result

# Read data from file. The format of the file is 3 columns : x y v
# where v is the value to plotted for coordinate (x,y)

Data = np.genfromtxt(input_file)
x = Data[:,0]
y = Data[:,1]
v = Data[:,2]

# Determine x and y limits and resolution of data

x_uniq = np.array(uniq(np.sort(x)))
y_uniq = np.array(uniq(np.sort(y)))

x_resolution = x_uniq.size
y_resolution = y_uniq.size

x_interval_length = x_uniq[1]-x_uniq[0]
y_interval_length = y_uniq[1]-y_uniq[0]

xmin = x.min()
xmax = x.max()+0.5*x_interval_length
ymin = y.min()
ymax = y.max()+0.5*y_interval_length

# Reshape 1D data to turn it into a 2D "image"

v = v.reshape([x_resolution, y_resolution])
v = v[:,range(y_resolution-1,-1,-1)].transpose()

# Plot 2D "image" 
# ---------------

# I use imshow which only work with linear axes.
# We will have to change the axes later...

axis_lim=[xmin, xmax, ymin, ymax]

fig = plt.figure()
ax = fig.add_subplot(111)
extent = [xmin, xmax, ymin, ymax]
img = plt.imshow(v, extent=extent, interpolation='nearest', cmap=cm.Reds, aspect='auto')
ax.axis(axis_lim)

# Make space for the colorbar
x2_bb_eff = (x2_bb-(cb_fraction+cb_pad)*x1_bb)/(1.0-(cb_fraction+cb_pad))
ax.set_position([x1_bb, y1_bb, x2_bb_eff-x1_bb, y2_bb-y1_bb])
position = ax.get_position()

# Remove axis ticks so that we can put log ticks on top
ax.set_xticks([])
ax.set_yticks([])

# Add colorbar
cb = fig.colorbar(img,fraction=cb_fraction,pad=cb_pad)
cb.set_label('Value [unit]')

# Add logarithmic axes
# --------------------

# Empty plot on top of previous one. Only used to add log axes.
ax = fig.add_subplot(111,frameon=False)
ax.set_xscale('log')
ax.set_yscale('log')
plt.plot([])
ax.set_position([x1_bb, y1_bb, x2_bb-x1_bb, y2_bb-y1_bb])

axis_lim_log=map(lambda x: 10.**x, axis_lim)
ax.axis(axis_lim_log)

plt.grid(b=True, which='major', linewidth=1)
plt.ylabel('Some quantity [unit]')
plt.xlabel('Another quantity [unit]')

plt.show()
3
ZoibergPatient 13 déc. 2011 à 17:16

D'après la docstring matplotlib.pyplot.hist, il semble qu'il y ait un argument 'log' à définir sur 'True' si vous voulez une échelle de log sur l'axe.

hist(x, bins=10, range=None, normed=False, cumulative=False,
     bottom=None, histtype='bar', align='mid',
     orientation='vertical', rwidth=None, log=False, **kwargs)

log:
If True, the histogram axis will be set to a log scale. If log is True and x is a 1D
array, empty bins will be filtered out and only the non-empty (n, bins, patches) will be
returned.

Il existe également une fonction pyplot.loglog pour créer un tracé avec une mise à l'échelle du journal sur les axes x et y.

4
gcalmettes 10 oct. 2011 à 16:57

La réponse de @gcalmettes fait référence à pyplot.hist. La signature de pyplot.hexbin est un peu différente:

hexbin(x, y, C = None, gridsize = 100, bins = None,
             xscale = 'linear', yscale = 'linear',
             cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None,
             edgecolors='none', reduce_C_function = np.mean, mincnt=None, marginals=True,
             **kwargs)

Vous êtes intéressé par le paramètre xscale:

*xscale*: [ 'linear' | 'log' ]
    Use a linear or log10 scale on the horizontal axis.
1
joaquin 28 nov. 2011 à 17:34
7688077