标签归档:matplotlib

在matplotlib上的散点图中为每个系列设置不同的颜色

问题:在matplotlib上的散点图中为每个系列设置不同的颜色

假设我有三个数据集:

X = [1,2,3,4]
Y1 = [4,8,12,16]
Y2 = [1,4,9,16]

我可以散点图:

from matplotlib import pyplot as plt
plt.scatter(X,Y1,color='red')
plt.scatter(X,Y2,color='blue')
plt.show()

我怎样用10套来做到这一点?

我进行了搜索,可以找到我所要求的任何参考。

编辑:澄清(希望)我的问题

如果我多次调用散点图,则只能在每个散点图上设置相同的颜色。另外,我知道我可以手动设置颜色阵列,但是我敢肯定有更好的方法可以做到这一点。我的问题是:“如何自动散布我的几个数据集,每个数据集具有不同的颜色。

如果有帮助,我可以轻松地为每个数据集分配一个唯一的编号。

Suppose I have three data sets:

X = [1,2,3,4]
Y1 = [4,8,12,16]
Y2 = [1,4,9,16]

I can scatter plot this:

from matplotlib import pyplot as plt
plt.scatter(X,Y1,color='red')
plt.scatter(X,Y2,color='blue')
plt.show()

How can I do this with 10 sets?

I searched for this and could find any reference to what I’m asking.

Edit: clarifying (hopefully) my question

If I call scatter multiple times, I can only set the same color on each scatter. Also, I know I can set a color array manually but I’m sure there is a better way to do this. My question is then, “How can I automatically scatter-plot my several data sets, each with a different color.

If that helps, I can easily assign a unique number to each data set.


回答 0

我不知道“手动”是什么意思。您可以选择一个颜色图并足够容易地创建颜色阵列:

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

x = np.arange(10)
ys = [i+x+(i*x)**2 for i in range(10)]

colors = cm.rainbow(np.linspace(0, 1, len(ys)))
for y, c in zip(ys, colors):
    plt.scatter(x, y, color=c)

或者,您可以使用itertools.cycle并指定要循环显示的颜色来制作自己的颜色循环仪,并使用next来获得所需的颜色。例如,使用3种颜色:

import itertools

colors = itertools.cycle(["r", "b", "g"])
for y in ys:
    plt.scatter(x, y, color=next(colors))

想一想,也许最好不要同时使用zip第一个:

colors = iter(cm.rainbow(np.linspace(0, 1, len(ys))))
for y in ys:
    plt.scatter(x, y, color=next(colors))

I don’t know what you mean by ‘manually’. You can choose a colourmap and make a colour array easily enough:

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

x = np.arange(10)
ys = [i+x+(i*x)**2 for i in range(10)]

colors = cm.rainbow(np.linspace(0, 1, len(ys)))
for y, c in zip(ys, colors):
    plt.scatter(x, y, color=c)

Or you can make your own colour cycler using itertools.cycle and specifying the colours you want to loop over, using next to get the one you want. For example, with 3 colours:

import itertools

colors = itertools.cycle(["r", "b", "g"])
for y in ys:
    plt.scatter(x, y, color=next(colors))

Come to think of it, maybe it’s cleaner not to use zip with the first one neither:

colors = iter(cm.rainbow(np.linspace(0, 1, len(ys))))
for y in ys:
    plt.scatter(x, y, color=next(colors))

回答 1

在matplotlib中用不同颜色的点绘制图的正常方法是传递颜色列表作为参数。

例如:

import matplotlib.pyplot
matplotlib.pyplot.scatter([1,2,3],[4,5,6],color=['red','green','blue'])

当您有一个列表列表时,您希望每个列表都带有颜色。我认为最优雅的方法是@DSM建议,只需做一个循环进行多次调用即可分散。

但是,如果由于某种原因您只想打一个电话,就可以制作一个大的颜色列表,并具有列表理解力和一些地板分割:

import matplotlib
import numpy as np

X = [1,2,3,4]
Ys = np.array([[4,8,12,16],
      [1,4,9,16],
      [17, 10, 13, 18],
      [9, 10, 18, 11],
      [4, 15, 17, 6],
      [7, 10, 8, 7],
      [9, 0, 10, 11],
      [14, 1, 15, 5],
      [8, 15, 9, 14],
       [20, 7, 1, 5]])
nCols = len(X)  
nRows = Ys.shape[0]

colors = matplotlib.cm.rainbow(np.linspace(0, 1, len(Ys)))

cs = [colors[i//len(X)] for i in range(len(Ys)*len(X))] #could be done with numpy's repmat
Xs=X*nRows #use list multiplication for repetition
matplotlib.pyplot.scatter(Xs,Ys.flatten(),color=cs)

cs = [array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 ...
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00])]

The normal way to plot plots with points in different colors in matplotlib is to pass a list of colors as a parameter.

E.g.:

import matplotlib.pyplot
matplotlib.pyplot.scatter([1,2,3],[4,5,6],color=['red','green','blue'])

When you have a list of lists and you want them colored per list. I think the most elegant way is that suggesyted by @DSM, just do a loop making multiple calls to scatter.

But if for some reason you wanted to do it with just one call, you can make a big list of colors, with a list comprehension and a bit of flooring division:

import matplotlib
import numpy as np

X = [1,2,3,4]
Ys = np.array([[4,8,12,16],
      [1,4,9,16],
      [17, 10, 13, 18],
      [9, 10, 18, 11],
      [4, 15, 17, 6],
      [7, 10, 8, 7],
      [9, 0, 10, 11],
      [14, 1, 15, 5],
      [8, 15, 9, 14],
       [20, 7, 1, 5]])
nCols = len(X)  
nRows = Ys.shape[0]

colors = matplotlib.cm.rainbow(np.linspace(0, 1, len(Ys)))

cs = [colors[i//len(X)] for i in range(len(Ys)*len(X))] #could be done with numpy's repmat
Xs=X*nRows #use list multiplication for repetition
matplotlib.pyplot.scatter(Xs,Ys.flatten(),color=cs)

cs = [array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 ...
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00])]

回答 2

一个简单的解决方法

如果您只有一种类型的集合(例如,没有误差线的散点图),则还可以在绘制它们后更改颜色,这有时更易于执行。

import matplotlib.pyplot as plt
from random import randint
import numpy as np

#Let's generate some random X, Y data X = [ [frst group],[second group] ...]
X = [ [randint(0,50) for i in range(0,5)] for i in range(0,24)]
Y = [ [randint(0,50) for i in range(0,5)] for i in range(0,24)]
labels = range(1,len(X)+1)

fig = plt.figure()
ax = fig.add_subplot(111)
for x,y,lab in zip(X,Y,labels):
        ax.scatter(x,y,label=lab)

您唯一需要的一段代码:

#Now this is actually the code that you need, an easy fix your colors just cut and paste not you need ax.
colormap = plt.cm.gist_ncar #nipy_spectral, Set1,Paired  
colorst = [colormap(i) for i in np.linspace(0, 0.9,len(ax.collections))]       
for t,j1 in enumerate(ax.collections):
    j1.set_color(colorst[t])


ax.legend(fontsize='small')

即使在同一子图中有许多不同的散点图,输出也会为您提供不同的颜色。

An easy fix

If you have only one type of collections (e.g. scatter with no error bars) you can also change the colours after that you have plotted them, this sometimes is easier to perform.

import matplotlib.pyplot as plt
from random import randint
import numpy as np

#Let's generate some random X, Y data X = [ [frst group],[second group] ...]
X = [ [randint(0,50) for i in range(0,5)] for i in range(0,24)]
Y = [ [randint(0,50) for i in range(0,5)] for i in range(0,24)]
labels = range(1,len(X)+1)

fig = plt.figure()
ax = fig.add_subplot(111)
for x,y,lab in zip(X,Y,labels):
        ax.scatter(x,y,label=lab)

The only piece of code that you need:

#Now this is actually the code that you need, an easy fix your colors just cut and paste not you need ax.
colormap = plt.cm.gist_ncar #nipy_spectral, Set1,Paired  
colorst = [colormap(i) for i in np.linspace(0, 0.9,len(ax.collections))]       
for t,j1 in enumerate(ax.collections):
    j1.set_color(colorst[t])


ax.legend(fontsize='small')

The output gives you differnent colors even when you have many different scatter plots in the same subplot.


回答 3

您可以始终plot()像这样使用该函数:

import matplotlib.pyplot as plt

import numpy as np

x = np.arange(10)
ys = [i+x+(i*x)**2 for i in range(10)]
plt.figure()
for y in ys:
    plt.plot(x, y, 'o')
plt.show()

You can always use the plot() function like so:

import matplotlib.pyplot as plt

import numpy as np

x = np.arange(10)
ys = [i+x+(i*x)**2 for i in range(10)]
plt.figure()
for y in ys:
    plt.plot(x, y, 'o')
plt.show()


回答 4

在2013年1月和matplotlib 1.3.1(2013年8月)之前,这个问题有点棘手,您可以在matpplotlib网站上找到最旧的稳定版本。但是在那之后,它是微不足道的。

因为当前版本的matplotlib.pylab.scatter支持分配:颜色名称字符串数组,带有颜色映射的浮点数数组,RGB或RGBA数组。

此答案表示@Oxinabox对在2015年更正2013年版本的我的无尽热情。


您有两个选择,可以在单个调用中使用具有多种颜色的scatter命令。

  1. 作为pylab.scatter命令支持,请使用RGBA数组执行所需的任何颜色;

  2. 早在2013年初,就没有办法这样做,因为该命令仅支持整个散点集合的单一颜色。当我执行10000行项目时,我想出了一个通用的解决方案来绕过它。所以它很俗气,但是我可以做任何形状,颜色,大小和透明的东西。此技巧也可以应用于绘制路径集合,线集合…。

该代码也受到的源代码的启发pyplot.scatter,我只是复制了散点图,而没有触发它绘制。

该命令pyplot.scatter返回一个PatchCollection对象,在文件“matplotlib / collections.py”私有变量_facecolorsCollection类和方法set_facecolors

因此,只要有散点可以绘制,就可以这样做:

# rgbaArr is a N*4 array of float numbers you know what I mean
# X is a N*2 array of coordinates
# axx is the axes object that current draw, you get it from
# axx = fig.gca()

# also import these, to recreate the within env of scatter command 
import matplotlib.markers as mmarkers
import matplotlib.transforms as mtransforms
from matplotlib.collections import PatchCollection
import matplotlib.markers as mmarkers
import matplotlib.patches as mpatches


# define this function
# m is a string of scatter marker, it could be 'o', 's' etc..
# s is the size of the point, use 1.0
# dpi, get it from axx.figure.dpi
def addPatch_point(m, s, dpi):
    marker_obj = mmarkers.MarkerStyle(m)
    path = marker_obj.get_path()
    trans = mtransforms.Affine2D().scale(np.sqrt(s*5)*dpi/72.0)
    ptch = mpatches.PathPatch(path, fill = True, transform = trans)
    return ptch

patches = []
# markerArr is an array of maker string, ['o', 's'. 'o'...]
# sizeArr is an array of size float, [1.0, 1.0. 0.5...]

for m, s in zip(markerArr, sizeArr):
    patches.append(addPatch_point(m, s, axx.figure.dpi))

pclt = PatchCollection(
                patches,
                offsets = zip(X[:,0], X[:,1]),
                transOffset = axx.transData)

pclt.set_transform(mtransforms.IdentityTransform())
pclt.set_edgecolors('none') # it's up to you
pclt._facecolors = rgbaArr

# in the end, when you decide to draw
axx.add_collection(pclt)
# and call axx's parent to draw_idle()

This question is a bit tricky before Jan 2013 and matplotlib 1.3.1 (Aug 2013), which is the oldest stable version you can find on matpplotlib website. But after that it is quite trivial.

Because present version of matplotlib.pylab.scatter support assigning: array of colour name string, array of float number with colour map, array of RGB or RGBA.

this answer is dedicate to @Oxinabox’s endless passion for correcting the 2013 version of myself in 2015.


you have two option of using scatter command with multiple colour in a single call.

  1. as pylab.scatter command support use RGBA array to do whatever colour you want;

  2. back in early 2013, there is no way to do so, since the command only support single colour for the whole scatter point collection. When I was doing my 10000-line project I figure out a general solution to bypass it. so it is very tacky, but I can do it in whatever shape, colour, size and transparent. this trick also could be apply to draw path collection, line collection….

the code is also inspired by the source code of pyplot.scatter, I just duplicated what scatter does without trigger it to draw.

the command pyplot.scatter return a PatchCollection Object, in the file “matplotlib/collections.py” a private variable _facecolors in Collection class and a method set_facecolors.

so whenever you have a scatter points to draw you can do this:

# rgbaArr is a N*4 array of float numbers you know what I mean
# X is a N*2 array of coordinates
# axx is the axes object that current draw, you get it from
# axx = fig.gca()

# also import these, to recreate the within env of scatter command 
import matplotlib.markers as mmarkers
import matplotlib.transforms as mtransforms
from matplotlib.collections import PatchCollection
import matplotlib.markers as mmarkers
import matplotlib.patches as mpatches


# define this function
# m is a string of scatter marker, it could be 'o', 's' etc..
# s is the size of the point, use 1.0
# dpi, get it from axx.figure.dpi
def addPatch_point(m, s, dpi):
    marker_obj = mmarkers.MarkerStyle(m)
    path = marker_obj.get_path()
    trans = mtransforms.Affine2D().scale(np.sqrt(s*5)*dpi/72.0)
    ptch = mpatches.PathPatch(path, fill = True, transform = trans)
    return ptch

patches = []
# markerArr is an array of maker string, ['o', 's'. 'o'...]
# sizeArr is an array of size float, [1.0, 1.0. 0.5...]

for m, s in zip(markerArr, sizeArr):
    patches.append(addPatch_point(m, s, axx.figure.dpi))

pclt = PatchCollection(
                patches,
                offsets = zip(X[:,0], X[:,1]),
                transOffset = axx.transData)

pclt.set_transform(mtransforms.IdentityTransform())
pclt.set_edgecolors('none') # it's up to you
pclt._facecolors = rgbaArr

# in the end, when you decide to draw
axx.add_collection(pclt)
# and call axx's parent to draw_idle()

回答 5

这对我有用:

对于每个系列,请使用随机的RGB颜色生成器

c = color[np.random.random_sample(), np.random.random_sample(), np.random.random_sample()]

This works for me:

for each series, use a random rgb colour generator

c = color[np.random.random_sample(), np.random.random_sample(), np.random.random_sample()]

回答 6

对于大型数据集和有限数量的颜色,一种更快的解决方案是使用Pandas和groupby函数:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time


# a generic set of data with associated colors
nsamples=1000
x=np.random.uniform(0,10,nsamples)
y=np.random.uniform(0,10,nsamples)
colors={0:'r',1:'g',2:'b',3:'k'}
c=[colors[i] for i in np.round(np.random.uniform(0,3,nsamples),0)]

plt.close('all')

# "Fast" Scatter plotting
starttime=time.time()
# 1) make a dataframe
df=pd.DataFrame()
df['x']=x
df['y']=y
df['c']=c
plt.figure()
# 2) group the dataframe by color and loop
for g,b in df.groupby(by='c'):
    plt.scatter(b['x'],b['y'],color=g)
print('Fast execution time:', time.time()-starttime)

# "Slow" Scatter plotting
starttime=time.time()
plt.figure()
# 2) group the dataframe by color and loop
for i in range(len(x)):
    plt.scatter(x[i],y[i],color=c[i])
print('Slow execution time:', time.time()-starttime)

plt.show()

A MUCH faster solution for large dataset and limited number of colors is the use of Pandas and the groupby function:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time


# a generic set of data with associated colors
nsamples=1000
x=np.random.uniform(0,10,nsamples)
y=np.random.uniform(0,10,nsamples)
colors={0:'r',1:'g',2:'b',3:'k'}
c=[colors[i] for i in np.round(np.random.uniform(0,3,nsamples),0)]

plt.close('all')

# "Fast" Scatter plotting
starttime=time.time()
# 1) make a dataframe
df=pd.DataFrame()
df['x']=x
df['y']=y
df['c']=c
plt.figure()
# 2) group the dataframe by color and loop
for g,b in df.groupby(by='c'):
    plt.scatter(b['x'],b['y'],color=g)
print('Fast execution time:', time.time()-starttime)

# "Slow" Scatter plotting
starttime=time.time()
plt.figure()
# 2) group the dataframe by color and loop
for i in range(len(x)):
    plt.scatter(x[i],y[i],color=c[i])
print('Slow execution time:', time.time()-starttime)

plt.show()

设置Matplotlib颜色条大小以匹配图形

问题:设置Matplotlib颜色条大小以匹配图形

我无法在像这样的imshow图上获得与该图相同的高度的色条,但事后没有使用Photoshop。如何获得与之匹配的高度?

I cannot get the colorbar on imshow graphs like this one to be the same height as the graph, short of using Photoshop after the fact. How do I get the heights to match?


回答 0

您可以使用matplotlib AxisDivider轻松完成此操作

链接页面中的示例也可以在不使用子图的情况下运行:

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np

plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))

# create an axes on the right side of ax. The width of cax will be 5%
# of ax and the padding between cax and ax will be fixed at 0.05 inch.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)

plt.colorbar(im, cax=cax)

You can do this easily with a matplotlib AxisDivider.

The example from the linked page also works without using subplots:

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np

plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))

# create an axes on the right side of ax. The width of cax will be 5%
# of ax and the padding between cax and ax will be fixed at 0.05 inch.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)

plt.colorbar(im, cax=cax)


回答 1

这种组合(以及接近这些值的组合)似乎“神奇地”对我起作用,无论显示大小如何,都可以将颜色条缩放到绘图。

plt.colorbar(im,fraction=0.046, pad=0.04)

它也不需要共享轴,这可以使绘图不成正方形。

This combination (and values near to these) seems to “magically” work for me to keep the colorbar scaled to the plot, no matter what size the display.

plt.colorbar(im,fraction=0.046, pad=0.04)

It also does not require sharing the axis which can get the plot out of square.


回答 2

@bogatron已经给出了matplotlib文档建议的答案产生正确的高度,但是它引入了另一个问题。现在,颜色条的宽度(以及颜色条和图之间的间隔)随图的宽度而变化。换句话说,颜色条的纵横比不再固定。

为了获得正确的高度给定的宽高比,您必须更深入地研究神秘的axes_grid1模块。

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
import numpy as np

aspect = 20
pad_fraction = 0.5

ax = plt.gca()
im = ax.imshow(np.arange(200).reshape((20, 10)))
divider = make_axes_locatable(ax)
width = axes_size.AxesY(ax, aspect=1./aspect)
pad = axes_size.Fraction(pad_fraction, width)
cax = divider.append_axes("right", size=width, pad=pad)
plt.colorbar(im, cax=cax)

请注意,这指定了颜色条的宽度与绘图高度(与宽度相反)之前的图形)。

现在可以将颜色条和图形之间的间距指定为颜色条宽度的一部分,恕我直言,IMHO比图形宽度的一部分有意义得多。

更新:

我在主题上创建了一个IPython笔记本,将上面的代码打包到一个易于重用的函数中:

import matplotlib.pyplot as plt
from mpl_toolkits import axes_grid1

def add_colorbar(im, aspect=20, pad_fraction=0.5, **kwargs):
    """Add a vertical color bar to an image plot."""
    divider = axes_grid1.make_axes_locatable(im.axes)
    width = axes_grid1.axes_size.AxesY(im.axes, aspect=1./aspect)
    pad = axes_grid1.axes_size.Fraction(pad_fraction, width)
    current_ax = plt.gca()
    cax = divider.append_axes("right", size=width, pad=pad)
    plt.sca(current_ax)
    return im.axes.figure.colorbar(im, cax=cax, **kwargs)

可以这样使用:

im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)

@bogatron already gave the answer suggested by the matplotlib docs, which produces the right height, but it introduces a different problem. Now the width of the colorbar (as well as the space between colorbar and plot) changes with the width of the plot. In other words, the aspect ratio of the colorbar is not fixed anymore.

To get both the right height and a given aspect ratio, you have to dig a bit deeper into the mysterious axes_grid1 module.

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
import numpy as np

aspect = 20
pad_fraction = 0.5

ax = plt.gca()
im = ax.imshow(np.arange(200).reshape((20, 10)))
divider = make_axes_locatable(ax)
width = axes_size.AxesY(ax, aspect=1./aspect)
pad = axes_size.Fraction(pad_fraction, width)
cax = divider.append_axes("right", size=width, pad=pad)
plt.colorbar(im, cax=cax)

Note that this specifies the width of the colorbar w.r.t. the height of the plot (in contrast to the width of the figure, as it was before).

The spacing between colorbar and plot can now be specified as a fraction of the width of the colorbar, which is IMHO a much more meaningful number than a fraction of the figure width.

UPDATE:

I created an IPython notebook on the topic, where I packed the above code into an easily re-usable function:

import matplotlib.pyplot as plt
from mpl_toolkits import axes_grid1

def add_colorbar(im, aspect=20, pad_fraction=0.5, **kwargs):
    """Add a vertical color bar to an image plot."""
    divider = axes_grid1.make_axes_locatable(im.axes)
    width = axes_grid1.axes_size.AxesY(im.axes, aspect=1./aspect)
    pad = axes_grid1.axes_size.Fraction(pad_fraction, width)
    current_ax = plt.gca()
    cax = divider.append_axes("right", size=width, pad=pad)
    plt.sca(current_ax)
    return im.axes.figure.colorbar(im, cax=cax, **kwargs)

It can be used like this:

im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)

回答 3

我感谢上述所有答案。然而,像一些答案和评论中指出,该axes_grid1模块不能地址GeoAxes,而调整fractionpadshrink,和其他类似参数不一定能给出非常精确的顺序,这真的令我烦恼。我认为,colorbar独自axes解决可能是解决所有提到的问题的更好解决方案。

import matplotlib.pyplot as plt
import numpy as np

fig=plt.figure()
ax = plt.axes()
im = ax.imshow(np.arange(100).reshape((10,10)))

# Create an axes for colorbar. The position of the axes is calculated based on the position of ax.
# You can change 0.01 to adjust the distance between the main image and the colorbar.
# You can change 0.02 to adjust the width of the colorbar.
# This practice is universal for both subplots and GeoAxes.

cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax) # Similar to fig.colorbar(im, cax = cax)

结果

后来,我发现matplotlib.pyplot.colorbar官方文档也提供了ax选项,这些选项是现有的轴,将为颜色栏提供空间。因此,它对于多个子图很有用,请参见下文。

fig, ax = plt.subplots(2,1,figsize=(12,8)) # Caution, figsize will also influence positions.
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
fig.colorbar(im1, ax=ax)

结果

同样,您也可以通过指定cax达到类似的效果,从我的角度来看,这是一种更准确的方法。

fig, ax = plt.subplots(2,1,figsize=(12,8))
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
cax = fig.add_axes([ax[1].get_position().x1-0.25,ax[1].get_position().y0,0.02,ax[0].get_position().y1-ax[1].get_position().y0])
fig.colorbar(im1, cax=cax)

结果

I appreciate all the answers above. However, like some answers and comments pointed out, the axes_grid1 module cannot address GeoAxes, whereas adjusting fraction, pad, shrink, and other similar parameters cannot necessarily give the very precise order, which really bothers me. I believe that giving the colorbar its own axes might be a better solution to address all the issues that have been mentioned.

Code

import matplotlib.pyplot as plt
import numpy as np

fig=plt.figure()
ax = plt.axes()
im = ax.imshow(np.arange(100).reshape((10,10)))

# Create an axes for colorbar. The position of the axes is calculated based on the position of ax.
# You can change 0.01 to adjust the distance between the main image and the colorbar.
# You can change 0.02 to adjust the width of the colorbar.
# This practice is universal for both subplots and GeoAxes.

cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax) # Similar to fig.colorbar(im, cax = cax)

Result

Later on, I find matplotlib.pyplot.colorbar official documentation also gives ax option, which are existing axes that will provide room for the colorbar. Therefore, it is useful for multiple subplots, see following.

Code

fig, ax = plt.subplots(2,1,figsize=(12,8)) # Caution, figsize will also influence positions.
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
fig.colorbar(im1, ax=ax)

Result

Again, you can also achieve similar effects by specifying cax, a more accurate way from my perspective.

Code

fig, ax = plt.subplots(2,1,figsize=(12,8))
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
cax = fig.add_axes([ax[1].get_position().x1-0.25,ax[1].get_position().y0,0.02,ax[0].get_position().y1-ax[1].get_position().y0])
fig.colorbar(im1, cax=cax)

Result


回答 4

当您创建 colorbar分数和/或收缩参数尝试。

从文件:

分数0.15; 用于颜色条的原始轴的比例

缩小1.0;缩小颜色条的分数

When you create the colorbar try using the fraction and/or shrink parameters.

From the documents:

fraction 0.15; fraction of original axes to use for colorbar

shrink 1.0; fraction by which to shrink the colorbar


回答 5

以上所有解决方案都是好的,但是我最喜欢@Steve和@bejota的解决方案,因为它们不涉及花哨的调用并且具有通用性。

通用的意思是适用于任何类型的轴,包括GeoAxes。例如,您已投影了要映射的轴:

projection = cartopy.crs.UTM(zone='17N')
ax = plt.axes(projection=projection)
im = ax.imshow(np.arange(200).reshape((20, 10)))

调用

cax = divider.append_axes("right", size=width, pad=pad)

将失败: KeyException: map_projection

因此,使用所有类型的轴处理颜色条大小的唯一通用方法是:

ax.colorbar(im, fraction=0.046, pad=0.04)

使用介于0.035到0.046之间的分数以获得最佳尺寸。但是,必须调整小数和paddig的值,以使其最适合您的绘图,并且根据颜色条的方向是垂直位置还是水平位置,它们的值将有所不同。

All the above solutions are good, but I like @Steve’s and @bejota’s the best as they do not involve fancy calls and are universal.

By universal I mean that works with any type of axes including GeoAxes. For example, it you have projected axes for mapping:

projection = cartopy.crs.UTM(zone='17N')
ax = plt.axes(projection=projection)
im = ax.imshow(np.arange(200).reshape((20, 10)))

a call to

cax = divider.append_axes("right", size=width, pad=pad)

will fail with: KeyException: map_projection

Therefore, the only universal way of dealing colorbar size with all types of axes is:

ax.colorbar(im, fraction=0.046, pad=0.04)

Work with fraction from 0.035 to 0.046 to get your best size. However, the values for the fraction and paddig will need to be adjusted to get the best fit for your plot and will differ depending if the orientation of the colorbar is in vertical position or horizontal.


减少地块刻度

问题:减少地块刻度

我的图表上有太多刻度线,它们彼此碰到。

如何减少刻度线的数量?

例如,我有勾号:

1E-6, 1E-5, 1E-4, ... 1E6, 1E7

我只想要:

1E-5, 1E-3, ... 1E5, 1E7

我曾经尝试过使用LogLocator,但是还无法弄清楚。

I have too many ticks on my graph and they are running into each other.

How can I reduce the number of ticks?

For example, I have ticks:

1E-6, 1E-5, 1E-4, ... 1E6, 1E7

And I only want:

1E-5, 1E-3, ... 1E5, 1E7

I’ve tried playing with the LogLocator, but I haven’t been able to figure this out.


回答 0

另外,如果您想简单地设置刻度线的数量,同时允许matplotlib对其进行定位(目前仅使用MaxNLocator),则有pyplot.locator_params

pyplot.locator_params(nbins=4)

您可以按照以下方法在此方法中指定特定的轴,默认为两者:

# To specify the number of ticks on both or any single axes
pyplot.locator_params(axis='y', nbins=6)
pyplot.locator_params(axis='x', nbins=10)

Alternatively, if you want to simply set the number of ticks while allowing matplotlib to position them (currently only with MaxNLocator), there is pyplot.locator_params,

pyplot.locator_params(nbins=4)

You can specify specific axis in this method as mentioned below, default is both:

# To specify the number of ticks on both or any single axes
pyplot.locator_params(axis='y', nbins=6)
pyplot.locator_params(axis='x', nbins=10)

回答 1

如果仍然有人在搜索结果中获得此页面:

fig, ax = plt.subplots()

plt.plot(...)

every_nth = 4
for n, label in enumerate(ax.xaxis.get_ticklabels()):
    if n % every_nth != 0:
        label.set_visible(False)

If somebody still gets this page in search results:

fig, ax = plt.subplots()

plt.plot(...)

every_nth = 4
for n, label in enumerate(ax.xaxis.get_ticklabels()):
    if n % every_nth != 0:
        label.set_visible(False)

回答 2

要解决刻度的自定义和外观问题,请参见matplotlib网站上的“刻度定位器”指南

ax.xaxis.set_major_locator(plt.MaxNLocator(3))

将x轴上的总刻度数设置为3,并将其均匀地分布在整个轴上。

还有一个很好的教程

To solve the issue of customisation and appearance of the ticks, see the Tick Locators guide on the matplotlib website

ax.xaxis.set_major_locator(plt.MaxNLocator(3))

Would set the total number of ticks in the x-axis to 3, and evenly distribute it across the axis.

There is also a nice tutorial about this


回答 3

set_ticks()轴对象有一个功能。

There’s a set_ticks() function for axis objects.


回答 4

万一有人仍然需要它,并且因为这里没有任何东西真正适合我,我想出了一种非常简单的方法,可以将生成的图的外观保持“原样”,同时将刻度数固定为N:

import numpy as np
import matplotlib.pyplot as plt

f, ax = plt.subplots()
ax.plot(range(100))

ymin, ymax = ax.get_ylim()
ax.set_yticks(np.round(np.linspace(ymin, ymax, N), 2))

in case somebody still needs it, and since nothing here really worked for me, i came up with a very simple way that keeps the appearance of the generated plot “as is” while fixing the number of ticks to exactly N:

import numpy as np
import matplotlib.pyplot as plt

f, ax = plt.subplots()
ax.plot(range(100))

ymin, ymax = ax.get_ylim()
ax.set_yticks(np.round(np.linspace(ymin, ymax, N), 2))

回答 5

@raphael提供的解决方案很简单,也很有帮助。

不过,显示的刻度标签将不是从原始分布采样的值,而是从所返回的数组索引中采样的值np.linspace(ymin, ymax, N)

若要显示与原始刻度标签均匀隔开的N个值,请使用set_yticklabels()方法。这是y轴的代码段,带有整数标签:

import numpy as np
import matplotlib.pyplot as plt

ax = plt.gca()

ymin, ymax = ax.get_ylim()
custom_ticks = np.linspace(ymin, ymax, N, dtype=int)
ax.set_yticks(custom_ticks)
ax.set_yticklabels(custom_ticks)

The solution @raphael gave is straightforward and quite helpful.

Still, the displayed tick labels will not be values sampled from the original distribution but from the indexes of the array returned by np.linspace(ymin, ymax, N).

To display N values evenly spaced from your original tick labels, use the set_yticklabels() method. Here is a snippet for the y axis, with integer labels:

import numpy as np
import matplotlib.pyplot as plt

ax = plt.gca()

ymin, ymax = ax.get_ylim()
custom_ticks = np.linspace(ymin, ymax, N, dtype=int)
ax.set_yticks(custom_ticks)
ax.set_yticklabels(custom_ticks)

回答 6

使用对数刻度时,可以使用以下命令固定主要刻度数

import matplotlib.pyplot as plt

....

plt.locator_params(numticks=12)
plt.show()

设置为的值numticks确定要显示的轴刻度数。

@bgamari的帖子中介绍了该locator_params()功能,但是使用对nticks数刻度时,该参数会引发错误。

When a log scale is used the number of major ticks can be fixed with the following command

import matplotlib.pyplot as plt

....

plt.locator_params(numticks=12)
plt.show()

The value set to numticks determines the number of axis ticks to be displayed.

Credits to @bgamari’s post for introducing the locator_params() function, but the nticks parameter throws an error when a log scale is used.


如何从matplotlib中删除框架(pyplot.figure与matplotlib.figure)(frameon = False在matplotlib中有问题)

问题:如何从matplotlib中删除框架(pyplot.figure与matplotlib.figure)(frameon = False在matplotlib中有问题)

为了删除图中的框架,我写

frameon=False

可以完美搭配使用pyplot.figure,但matplotlib.Figure只能去除灰色背景,框架保持不变。另外,我只希望显示线条,其余所有图都是透明的。

使用pyplot我可以做我想做的事,我想用matplotlib做它有很长的原因,我不想提及扩展我的问题。

To remove frame in figure, I write

frameon=False

works perfect with pyplot.figure, but with matplotlib.Figure it only removes the gray background, the frame stays . Also, I only want the lines to show, and all the rest of figure be transparent.

with pyplot I can do what I want, I want to do it with matplotlib for some long reason I ‘d rather not mention to extend my question.


回答 0

首先,如果您使用savefig,请注意,除非另外指定(例如fig.savefig('blah.png', transparent=True)),否则保存时它将覆盖图形的背景颜色。

但是,要在屏幕上删除轴和图形的背景,您需要同时设置它们ax.patchfig.patch使其不可见。

例如

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(range(10))

for item in [fig, ax]:
    item.patch.set_visible(False)

with open('test.png', 'w') as outfile:
    fig.canvas.print_png(outfile)

(当然,您不能说出SO的白色背景有什么区别,但是一切都是透明的…)

如果您不想显示该线以外的任何东西,也可以使用关闭轴ax.axis('off')

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(range(10))

fig.patch.set_visible(False)
ax.axis('off')

with open('test.png', 'w') as outfile:
    fig.canvas.print_png(outfile)

但是,在这种情况下,您可能希望使轴占据整个图形。如果您手动指定轴的位置,则可以告诉它占用完整的数字(或者,可以使用subplots_adjust,但是对于单轴来说更简单)。

import matplotlib.pyplot as plt

fig = plt.figure(frameon=False)
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')

ax.plot(range(10))

with open('test.png', 'w') as outfile:
    fig.canvas.print_png(outfile)

First off, if you’re using savefig, be aware that it will override the figure’s background color when saving unless you specify otherwise (e.g. fig.savefig('blah.png', transparent=True)).

However, to remove the axes’ and figure’s background on-screen, you’ll need to set both ax.patch and fig.patch to be invisible.

E.g.

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(range(10))

for item in [fig, ax]:
    item.patch.set_visible(False)

with open('test.png', 'w') as outfile:
    fig.canvas.print_png(outfile)

(Of course, you can’t tell the difference on SO’s white background, but everything is transparent…)

If you don’t want to show anything other than the line, turn the axis off as well using ax.axis('off'):

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(range(10))

fig.patch.set_visible(False)
ax.axis('off')

with open('test.png', 'w') as outfile:
    fig.canvas.print_png(outfile)

In that case, though, you may want to make the axes take up the full figure. If you manually specify the location of the axes, you can tell it to take up the full figure (alternately, you can use subplots_adjust, but this is simpler for the case of a single axes).

import matplotlib.pyplot as plt

fig = plt.figure(frameon=False)
ax = fig.add_axes([0, 0, 1, 1])
ax.axis('off')

ax.plot(range(10))

with open('test.png', 'w') as outfile:
    fig.canvas.print_png(outfile)


回答 1

ax.axis('off'),如乔·肯顿(Joe Kington)所指出的那样,请删除除画线外的所有内容。

对于那些只希望删除框架(边框)并保留标签,股票行情指示器等的人,可以通过访问spines轴上的对象来做到这一点。给定轴对象ax,以下内容应删除所有四个边的边界:

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)

并且,从情节中删除x和剔除y

 ax.get_xaxis().set_ticks([])
 ax.get_yaxis().set_ticks([])

ax.axis('off'), will as Joe Kington pointed out, remove everything except the plotted line.

For those wanting to only remove the frame (border), and keep labels, tickers etc, one can do that by accessing the spines object on the axis. Given an axis object ax, the following should remove borders on all four sides:

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)

And, in case of removing x and y ticks from the plot:

 ax.get_xaxis().set_ticks([])
 ax.get_yaxis().set_ticks([])

回答 2

摆脱matplotlib较新版本中丑陋框架的最简单方法:

import matplotlib.pyplot as plt
plt.box(False)

如果确实必须始终使用面向对象的方法,请执行:ax.set_frame_on(False)

The easiest way to get rid of the the ugly frame in newer versions of matplotlib:

import matplotlib.pyplot as plt
plt.box(False)

If you really must always use the object oriented approach, then do: ax.set_frame_on(False).


回答 3

@peeol的出色答案为基础,您也可以通过以下方法删除框架

for spine in plt.gca().spines.values():
    spine.set_visible(False)

举个例子(整个代码示例可以在本文的结尾处找到),假设您有一个这样的条形图,

您可以使用上面的命令删除框架,然后保留x-ytick标签(未显示图)或也删除它们

plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='off', labelbottom='on')

在这种情况下,可以直接在条形上贴标签;最终的图看起来像这样(代码可以在下面找到):

这是生成图所必需的全部代码:

import matplotlib.pyplot as plt
import numpy as np

plt.figure()

xvals = list('ABCDE')
yvals = np.array(range(1, 6))

position = np.arange(len(xvals))

mybars = plt.bar(position, yvals, align='center', linewidth=0)
plt.xticks(position, xvals)

plt.title('My great data')
# plt.show()

# get rid of the frame
for spine in plt.gca().spines.values():
    spine.set_visible(False)

# plt.show()
# remove all the ticks and directly label each bar with respective value
plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='off', labelbottom='on')

# plt.show()

# direct label each bar with Y axis values
for bari in mybars:
    height = bari.get_height()
    plt.gca().text(bari.get_x() + bari.get_width()/2, bari.get_height()-0.2, str(int(height)),
                 ha='center', color='white', fontsize=15)
plt.show()

Building up on @peeol’s excellent answer, you can also remove the frame by doing

for spine in plt.gca().spines.values():
    spine.set_visible(False)

To give an example (the entire code sample can be found at the end of this post), let’s say you have a bar plot like this,

you can remove the frame with the commands above and then either keep the x- and ytick labels (plot not shown) or remove them as well doing

plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='off', labelbottom='on')

In this case, one can then label the bars directly; the final plot could look like this (code can be found below):

Here is the entire code that is necessary to generate the plots:

import matplotlib.pyplot as plt
import numpy as np

plt.figure()

xvals = list('ABCDE')
yvals = np.array(range(1, 6))

position = np.arange(len(xvals))

mybars = plt.bar(position, yvals, align='center', linewidth=0)
plt.xticks(position, xvals)

plt.title('My great data')
# plt.show()

# get rid of the frame
for spine in plt.gca().spines.values():
    spine.set_visible(False)

# plt.show()
# remove all the ticks and directly label each bar with respective value
plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='off', labelbottom='on')

# plt.show()

# direct label each bar with Y axis values
for bari in mybars:
    height = bari.get_height()
    plt.gca().text(bari.get_x() + bari.get_width()/2, bari.get_height()-0.2, str(int(height)),
                 ha='center', color='white', fontsize=15)
plt.show()

回答 4

正如我在这里回答的那样,您可以通过样式设置(样式表或rcParams)从所有绘图中删除刺:

import matplotlib as mpl

mpl.rcParams['axes.spines.left'] = False
mpl.rcParams['axes.spines.right'] = False
mpl.rcParams['axes.spines.top'] = False
mpl.rcParams['axes.spines.bottom'] = False

As I answered here, you can remove spines from all your plots through style settings (style sheet or rcParams):

import matplotlib as mpl

mpl.rcParams['axes.spines.left'] = False
mpl.rcParams['axes.spines.right'] = False
mpl.rcParams['axes.spines.top'] = False
mpl.rcParams['axes.spines.bottom'] = False

回答 5

问题

我在使用轴时遇到了类似的问题。class参数为,frameon但kwarg为frame_onaxes_api
>>> plt.gca().set(frameon=False)
AttributeError: Unknown property frameon

frame_on

data = range(100)
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(data)
#ax.set(frameon=False)  # Old
ax.set(frame_on=False)  # New
plt.show()

Problem

I had a similar problem using axes. The class parameter is frameon but the kwarg is frame_on. axes_api
>>> plt.gca().set(frameon=False)
AttributeError: Unknown property frameon

Solution

frame_on

Example

data = range(100)
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(data)
#ax.set(frameon=False)  # Old
ax.set(frame_on=False)  # New
plt.show()

回答 6

我经常这样做:

from pylab import *
axes(frameon = 0)
...
show()

I use to do so:

from pylab import *
axes(frameon = 0)
...
show()

回答 7

删除图表框架

for spine in plt.gca().spines.values():
  spine.set_visible(False)

我希望这可以工作

To remove the frame of the chart

for spine in plt.gca().spines.values():
  spine.set_visible(False)

I hope this could work


回答 8

df = pd.DataFrame({
'client_scripting_ms' : client_scripting_ms,
 'apimlayer' : apimlayer, 'server' : server
}, index = index)

ax = df.plot(kind = 'barh', 
     stacked = True,
     title = "Chart",
     width = 0.20, 
     align='center', 
     figsize=(7,5))

plt.legend(loc='upper right', frameon=True)

ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('right')
df = pd.DataFrame({
'client_scripting_ms' : client_scripting_ms,
 'apimlayer' : apimlayer, 'server' : server
}, index = index)

ax = df.plot(kind = 'barh', 
     stacked = True,
     title = "Chart",
     width = 0.20, 
     align='center', 
     figsize=(7,5))

plt.legend(loc='upper right', frameon=True)

ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('right')

回答 9

plt.box(False)
plt.xticks([])
plt.yticks([])
plt.savefig('fig.png')

应该可以。

plt.box(False)
plt.xticks([])
plt.yticks([])
plt.savefig('fig.png')

should do the trick.


回答 10

plt.axis('off')
plt.savefig(file_path, bbox_inches="tight", pad_inches = 0)

plt.savefig本身具有这些选项,只需要在关闭轴之前

plt.axis('off')
plt.savefig(file_path, bbox_inches="tight", pad_inches = 0)

plt.savefig has those options in itself, just need to set axes off before


用pyplot画一个圆

问题:用pyplot画一个圆

令人惊讶的是,我没有找到关于如何使用matplotlib.pyplot(请不要使用pylab)绘制圆作为输入中心(x,y)和半径r的简单描述。我尝试了一些变体:

import matplotlib.pyplot as plt
circle=plt.Circle((0,0),2)
# here must be something like circle.plot() or not?
plt.show()

…但是仍然无法正常工作。

surprisingly I didn’t find a straight-forward description on how to draw a circle with matplotlib.pyplot (please no pylab) taking as input center (x,y) and radius r. I tried some variants of this:

import matplotlib.pyplot as plt
circle=plt.Circle((0,0),2)
# here must be something like circle.plot() or not?
plt.show()

… but still didn’t get it working.


回答 0

您需要将其添加到轴。A Circle是的子类Artist,并且axes具有add_artist方法。

这是执行此操作的示例:

import matplotlib.pyplot as plt

circle1 = plt.Circle((0, 0), 0.2, color='r')
circle2 = plt.Circle((0.5, 0.5), 0.2, color='blue')
circle3 = plt.Circle((1, 1), 0.2, color='g', clip_on=False)

fig, ax = plt.subplots() # note we must use plt.subplots, not plt.subplot
# (or if you have an existing figure)
# fig = plt.gcf()
# ax = fig.gca()

ax.add_artist(circle1)
ax.add_artist(circle2)
ax.add_artist(circle3)

fig.savefig('plotcircles.png')

结果如下图:

第一个圆是原点,但默认情况下clip_onTrue,因此,只要圆超出,就会对其进行裁剪axes。第三个(绿色)圆圈显示了不剪切时会发生的情况Artist。它超出轴(但不超出图形,即图形大小不会自动调整以绘制所有艺术家)。

默认情况下,x,y和半径的单位对应于数据单位。在这种情况下,我没有在轴上绘制任何内容(fig.gca()返回当前轴),并且由于从未设置极限,因此它们的默认x和y范围为0到1。

这是该示例的继续,显示了单位的重要性:

circle1 = plt.Circle((0, 0), 2, color='r')
# now make a circle with no fill, which is good for hi-lighting key results
circle2 = plt.Circle((5, 5), 0.5, color='b', fill=False)
circle3 = plt.Circle((10, 10), 2, color='g', clip_on=False)

ax = plt.gca()
ax.cla() # clear things for fresh plot

# change default range so that new circles will work
ax.set_xlim((0, 10))
ax.set_ylim((0, 10))
# some data
ax.plot(range(11), 'o', color='black')
# key data point that we are encircling
ax.plot((5), (5), 'o', color='y')

ax.add_artist(circle1)
ax.add_artist(circle2)
ax.add_artist(circle3)
fig.savefig('plotcircles2.png')

结果是:

您会看到如何将第二个圆的填充设置为False,这对于环绕关键结果(例如我的黄色数据点)很有用。

You need to add it to an axes. A Circle is a subclass of an Artist, and an axes has an add_artist method.

Here’s an example of doing this:

import matplotlib.pyplot as plt

circle1 = plt.Circle((0, 0), 0.2, color='r')
circle2 = plt.Circle((0.5, 0.5), 0.2, color='blue')
circle3 = plt.Circle((1, 1), 0.2, color='g', clip_on=False)

fig, ax = plt.subplots() # note we must use plt.subplots, not plt.subplot
# (or if you have an existing figure)
# fig = plt.gcf()
# ax = fig.gca()

ax.add_artist(circle1)
ax.add_artist(circle2)
ax.add_artist(circle3)

fig.savefig('plotcircles.png')

This results in the following figure:

The first circle is at the origin, but by default clip_on is True, so the circle is clipped when ever it extends beyond the axes. The third (green) circle shows what happens when you don’t clip the Artist. It extends beyond the axes (but not beyond the figure, ie the figure size is not automatically adjusted to plot all of your artists).

The units for x, y and radius correspond to data units by default. In this case, I didn’t plot anything on my axes (fig.gca() returns the current axes), and since the limits have never been set, they defaults to an x and y range from 0 to 1.

Here’s a continuation of the example, showing how units matter:

circle1 = plt.Circle((0, 0), 2, color='r')
# now make a circle with no fill, which is good for hi-lighting key results
circle2 = plt.Circle((5, 5), 0.5, color='b', fill=False)
circle3 = plt.Circle((10, 10), 2, color='g', clip_on=False)

ax = plt.gca()
ax.cla() # clear things for fresh plot

# change default range so that new circles will work
ax.set_xlim((0, 10))
ax.set_ylim((0, 10))
# some data
ax.plot(range(11), 'o', color='black')
# key data point that we are encircling
ax.plot((5), (5), 'o', color='y')

ax.add_artist(circle1)
ax.add_artist(circle2)
ax.add_artist(circle3)
fig.savefig('plotcircles2.png')

which results in:

You can see how I set the fill of the 2nd circle to False, which is useful for encircling key results (like my yellow data point).


回答 1

import matplotlib.pyplot as plt
circle1=plt.Circle((0,0),.2,color='r')
plt.gcf().gca().add_artist(circle1)

快速精简版已接受答案,可将圆快速插入现有图中。请参阅接受的答案和其他答案以了解详细信息。

顺便说说:

  • gcf() 表示获取当前图形
  • gca() 表示获取当前轴
import matplotlib.pyplot as plt
circle1=plt.Circle((0,0),.2,color='r')
plt.gcf().gca().add_artist(circle1)

A quick condensed version of the accepted answer, to quickly plug a circle into an existing plot. Refer to the accepted answer and other answers to understand the details.

By the way:

  • gcf() means Get Current Figure
  • gca() means Get Current Axis

回答 2

如果要绘制一组圆,则可能需要查看此帖子要点(较新)。该帖子提供了一个名为的功能circles

该函数的circles工作方式类似于scatter,但是绘制圆的大小以数据单位表示。

这是一个例子:

from pylab import *
figure(figsize=(8,8))
ax=subplot(aspect='equal')

#plot one circle (the biggest one on bottom-right)
circles(1, 0, 0.5, 'r', alpha=0.2, lw=5, edgecolor='b', transform=ax.transAxes)

#plot a set of circles (circles in diagonal)
a=arange(11)
out = circles(a, a, a*0.2, c=a, alpha=0.5, edgecolor='none')
colorbar(out)

xlim(0,10)
ylim(0,10)

If you want to plot a set of circles, you might want to see this post or this gist(a bit newer). The post offered a function named circles.

The function circles works like scatter, but the sizes of plotted circles are in data unit.

Here’s an example:

from pylab import *
figure(figsize=(8,8))
ax=subplot(aspect='equal')

#plot one circle (the biggest one on bottom-right)
circles(1, 0, 0.5, 'r', alpha=0.2, lw=5, edgecolor='b', transform=ax.transAxes)

#plot a set of circles (circles in diagonal)
a=arange(11)
out = circles(a, a, a*0.2, c=a, alpha=0.5, edgecolor='none')
colorbar(out)

xlim(0,10)
ylim(0,10)


回答 3

#!/usr/bin/python
import matplotlib.pyplot as plt
import numpy as np

def xy(r,phi):
  return r*np.cos(phi), r*np.sin(phi)

fig = plt.figure()
ax = fig.add_subplot(111,aspect='equal')  

phis=np.arange(0,6.28,0.01)
r =1.
ax.plot( *xy(r,phis), c='r',ls='-' )
plt.show()

或者,如果你愿意的话,看看pathS,http://matplotlib.sourceforge.net/users/path_tutorial.html

#!/usr/bin/python
import matplotlib.pyplot as plt
import numpy as np

def xy(r,phi):
  return r*np.cos(phi), r*np.sin(phi)

fig = plt.figure()
ax = fig.add_subplot(111,aspect='equal')  

phis=np.arange(0,6.28,0.01)
r =1.
ax.plot( *xy(r,phis), c='r',ls='-' )
plt.show()

Or, if you prefer, look at the paths, http://matplotlib.sourceforge.net/users/path_tutorial.html


回答 4

如果无论数据坐标是什么,都希望“圆”的视觉长宽比保持为1,则可以使用scatter()方法。http://matplotlib.org/1.3.1/api/pyplot_api.html#matplotlib.pyplot.scatter

import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [10, 20, 30, 40, 50]
r = [100, 80, 60, 40, 20] # in points, not data units
fig, ax = plt.subplots(1, 1)
ax.scatter(x, y, s=r)
fig.show()

If you aim to have the “circle” maintain a visual aspect ratio of 1 no matter what the data coordinates are, you could use the scatter() method. http://matplotlib.org/1.3.1/api/pyplot_api.html#matplotlib.pyplot.scatter

import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [10, 20, 30, 40, 50]
r = [100, 80, 60, 40, 20] # in points, not data units
fig, ax = plt.subplots(1, 1)
ax.scatter(x, y, s=r)
fig.show()


回答 5

将常见问题扩展为可接受的答案。特别是:

  1. 以自然的长宽比查看圈子。

  2. 自动扩展轴限制以包括新绘制的圆。

独立的示例:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.add_patch(plt.Circle((0, 0), 0.2, color='r', alpha=0.5))
ax.add_patch(plt.Circle((1, 1), 0.5, color='#00ffff', alpha=0.5))
ax.add_artist(plt.Circle((1, 0), 0.5, color='#000033', alpha=0.5))

#Use adjustable='box-forced' to make the plot area square-shaped as well.
ax.set_aspect('equal', adjustable='datalim')
ax.plot()   #Causes an autoscale update.
plt.show()

注意两者之间ax.add_patch(..)和的区别ax.add_artist(..):只有前者使自动缩放机制考虑了这个圆(参考:Discussion),因此在运行上面的代码后,我们得到:

另请参阅:set_aspect(..)文档

Extending the accepted answer for a common usecase. In particular:

  1. View the circles at a natural aspect ratio.

  2. Automatically extend the axes limits to include the newly plotted circles.

Self-contained example:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.add_patch(plt.Circle((0, 0), 0.2, color='r', alpha=0.5))
ax.add_patch(plt.Circle((1, 1), 0.5, color='#00ffff', alpha=0.5))
ax.add_artist(plt.Circle((1, 0), 0.5, color='#000033', alpha=0.5))

#Use adjustable='box-forced' to make the plot area square-shaped as well.
ax.set_aspect('equal', adjustable='datalim')
ax.plot()   #Causes an autoscale update.
plt.show()

Note the difference between ax.add_patch(..) and ax.add_artist(..): of the two, only the former makes autoscaling machinery take the circle into account (reference: discussion), so after running the above code we get:

See also: set_aspect(..) documentation.


回答 6

我看到使用(.circle)的图,但是根据您可能想做的事情,您也可以尝试以下操作:

import matplotlib.pyplot as plt
import numpy as np

x = list(range(1,6))
y = list(range(10, 20, 2))

print(x, y)

for i, data in enumerate(zip(x,y)):
    j, k = data
    plt.scatter(j,k, marker = "o", s = ((i+1)**4)*50, alpha = 0.3)

centers = np.array([[5,18], [3,14], [7,6]])
m, n = make_blobs(n_samples=20, centers=[[5,18], [3,14], [7,6]], n_features=2, 
cluster_std = 0.4)
colors = ['g', 'b', 'r', 'm']

plt.figure(num=None, figsize=(7,6), facecolor='w', edgecolor='k')
plt.scatter(m[:,0], m[:,1])

for i in range(len(centers)):

    plt.scatter(centers[i,0], centers[i,1], color = colors[i], marker = 'o', s = 13000, alpha = 0.2)
    plt.scatter(centers[i,0], centers[i,1], color = 'k', marker = 'x', s = 50)

plt.savefig('plot.png')

I see plots with the use of (.circle) but based on what you might want to do you can also try this out:

import matplotlib.pyplot as plt
import numpy as np

x = list(range(1,6))
y = list(range(10, 20, 2))

print(x, y)

for i, data in enumerate(zip(x,y)):
    j, k = data
    plt.scatter(j,k, marker = "o", s = ((i+1)**4)*50, alpha = 0.3)

centers = np.array([[5,18], [3,14], [7,6]])
m, n = make_blobs(n_samples=20, centers=[[5,18], [3,14], [7,6]], n_features=2, 
cluster_std = 0.4)
colors = ['g', 'b', 'r', 'm']

plt.figure(num=None, figsize=(7,6), facecolor='w', edgecolor='k')
plt.scatter(m[:,0], m[:,1])

for i in range(len(centers)):

    plt.scatter(centers[i,0], centers[i,1], color = colors[i], marker = 'o', s = 13000, alpha = 0.2)
    plt.scatter(centers[i,0], centers[i,1], color = 'k', marker = 'x', s = 50)

plt.savefig('plot.png')


回答 7

您好,我已经写了一个画圆的代码。这将有助于绘制各种圆圈。 该图显示了半径为1且圆心为0,0 的圆。可以选择任意中心和半径。

## Draw a circle with center and radius defined
## Also enable the coordinate axes
import matplotlib.pyplot as plt
import numpy as np
# Define limits of coordinate system
x1 = -1.5
x2 = 1.5
y1 = -1.5
y2 = 1.5

circle1 = plt.Circle((0,0),1, color = 'k', fill = False, clip_on = False)
fig, ax = plt.subplots()
ax.add_artist(circle1)
plt.axis("equal")
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
plt.xlim(left=x1)
plt.xlim(right=x2)
plt.ylim(bottom=y1)
plt.ylim(top=y2)
plt.axhline(linewidth=2, color='k')
plt.axvline(linewidth=2, color='k')

##plt.grid(True)
plt.grid(color='k', linestyle='-.', linewidth=0.5)
plt.show()

祝好运

Hello I have written a code for drawing a circle. It will help for drawing all kind of circles. The image shows the circle with radius 1 and center at 0,0 The center and radius can be edited of any choice.

## Draw a circle with center and radius defined
## Also enable the coordinate axes
import matplotlib.pyplot as plt
import numpy as np
# Define limits of coordinate system
x1 = -1.5
x2 = 1.5
y1 = -1.5
y2 = 1.5

circle1 = plt.Circle((0,0),1, color = 'k', fill = False, clip_on = False)
fig, ax = plt.subplots()
ax.add_artist(circle1)
plt.axis("equal")
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
plt.xlim(left=x1)
plt.xlim(right=x2)
plt.ylim(bottom=y1)
plt.ylim(top=y2)
plt.axhline(linewidth=2, color='k')
plt.axvline(linewidth=2, color='k')

##plt.grid(True)
plt.grid(color='k', linestyle='-.', linewidth=0.5)
plt.show()

Good luck


回答 8

与散点图类似,您也可以使用具有圆线样式的法线图。使用markersize参数可以调整圆的半径:

import matplotlib.pyplot as plt

plt.plot(200, 2, 'o', markersize=7)

Similarly to scatter plot you can also use normal plot with circle line style. Using markersize parameter you can adjust radius of a circle:

import matplotlib.pyplot as plt

plt.plot(200, 2, 'o', markersize=7)

如何在matplotlib中更新图?

问题:如何在matplotlib中更新图?

我在这里重新绘制图形时遇到问题。我允许用户在时间刻度(x轴)中指定单位,然后重新计算并调用此函数plots()。我希望该图可以简单地更新,而不是将另一个图添加到该图上。

def plots():
    global vlgaBuffSorted
    cntr()

    result = collections.defaultdict(list)
    for d in vlgaBuffSorted:
        result[d['event']].append(d)

    result_list = result.values()

    f = Figure()
    graph1 = f.add_subplot(211)
    graph2 = f.add_subplot(212,sharex=graph1)

    for item in result_list:
        tL = []
        vgsL = []
        vdsL = []
        isubL = []
        for dict in item:
            tL.append(dict['time'])
            vgsL.append(dict['vgs'])
            vdsL.append(dict['vds'])
            isubL.append(dict['isub'])
        graph1.plot(tL,vdsL,'bo',label='a')
        graph1.plot(tL,vgsL,'rp',label='b')
        graph2.plot(tL,isubL,'b-',label='c')

    plotCanvas = FigureCanvasTkAgg(f, pltFrame)
    toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
    toolbar.pack(side=BOTTOM)
    plotCanvas.get_tk_widget().pack(side=TOP)

I’m having issues with redrawing the figure here. I allow the user to specify the units in the time scale (x-axis) and then I recalculate and call this function plots(). I want the plot to simply update, not append another plot to the figure.

def plots():
    global vlgaBuffSorted
    cntr()

    result = collections.defaultdict(list)
    for d in vlgaBuffSorted:
        result[d['event']].append(d)

    result_list = result.values()

    f = Figure()
    graph1 = f.add_subplot(211)
    graph2 = f.add_subplot(212,sharex=graph1)

    for item in result_list:
        tL = []
        vgsL = []
        vdsL = []
        isubL = []
        for dict in item:
            tL.append(dict['time'])
            vgsL.append(dict['vgs'])
            vdsL.append(dict['vds'])
            isubL.append(dict['isub'])
        graph1.plot(tL,vdsL,'bo',label='a')
        graph1.plot(tL,vgsL,'rp',label='b')
        graph2.plot(tL,isubL,'b-',label='c')

    plotCanvas = FigureCanvasTkAgg(f, pltFrame)
    toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
    toolbar.pack(side=BOTTOM)
    plotCanvas.get_tk_widget().pack(side=TOP)

回答 0

您基本上有两个选择:

  1. 完全执行当前操作,但是在重新配置数据之前先调用graph1.clear()graph2.clear()。这是最慢,但最简单和最可靠的选择。

  2. 除了重新绘制外,您还可以更新绘图对象的数据。您需要在代码中进行一些更改,但这应该比每次重新绘制都快得多。但是,您要绘制的数据的形状无法更改,并且如果数据范围正在更改,则需要手动重置x和y轴限制。

举一个第二种选择的例子:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)

# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma

for phase in np.linspace(0, 10*np.pi, 500):
    line1.set_ydata(np.sin(x + phase))
    fig.canvas.draw()
    fig.canvas.flush_events()

You essentially have two options:

  1. Do exactly what you’re currently doing, but call graph1.clear() and graph2.clear() before replotting the data. This is the slowest, but most simplest and most robust option.

  2. Instead of replotting, you can just update the data of the plot objects. You’ll need to make some changes in your code, but this should be much, much faster than replotting things every time. However, the shape of the data that you’re plotting can’t change, and if the range of your data is changing, you’ll need to manually reset the x and y axis limits.

To give an example of the second option:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)

# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma

for phase in np.linspace(0, 10*np.pi, 500):
    line1.set_ydata(np.sin(x + phase))
    fig.canvas.draw()
    fig.canvas.flush_events()

回答 1

您还可以执行以下操作:这将在for循环的50个循环的绘图上绘制10×1随机矩阵数据。

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()

You can also do like the following: This will draw a 10×1 random matrix data on the plot for 50 cycles of the for loop.

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()

回答 2

这对我有用。每次重复调用更新图形的函数。

import matplotlib.pyplot as plt
import matplotlib.animation as anim

def plot_cont(fun, xmax):
    y = []
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    def update(i):
        yi = fun()
        y.append(yi)
        x = range(len(y))
        ax.clear()
        ax.plot(x, y)
        print i, ': ', yi

    a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
    plt.show()

“ fun”是一个返回整数的函数。FuncAnimation将反复调用“更新”,它将执行“ xmax”次。

This worked for me. Repeatedly calls a function updating the graph every time.

import matplotlib.pyplot as plt
import matplotlib.animation as anim

def plot_cont(fun, xmax):
    y = []
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    def update(i):
        yi = fun()
        y.append(yi)
        x = range(len(y))
        ax.clear()
        ax.plot(x, y)
        print i, ': ', yi

    a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
    plt.show()

“fun” is a function that returns an integer. FuncAnimation will repeatedly call “update”, it will do that “xmax” times.


回答 3

如果有人碰到本文来寻找我想要的东西,我在

如何使用Matplotlib可视化标量2D数据?

http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop(在web.archive.org上)

然后对其进行修改,以将imshow与输入的帧堆栈一起使用,而不是动态生成和使用轮廓。


从3D形状的图像阵列(nBins,nBins,nBins)开始,称为frames

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

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

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

我还发现了进行此整个过程的简单得多的方法,尽管不够健壮:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

请注意,这两个似乎只适用于ipython --pylab=tkbackend = TkAgg

感谢您提供的所有帮助。

In case anyone comes across this article looking for what I was looking for, I found examples at

How to visualize scalar 2D data with Matplotlib?

and

http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop (on web.archive.org)

then modified them to use imshow with an input stack of frames, instead of generating and using contours on the fly.


Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

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

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

I also found a much simpler way to go about this whole process, albeit less robust:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg

Thank you for the help with everything.


回答 4

我发布了一个名为python-drawnow的软件包,该软件包提供了使图形更新的功能(通常在for循环内调用),类似于Matlab的drawnow

用法示例:

from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
    # can be arbitrarily complex; just to draw a figure
    #figure() # don't call!
    plot(t, x)
    #show() # don't call!

N = 1e3
figure() # call here instead!
ion()    # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
    x = sin(2 * pi * i**2 * t / 100.0)
    drawnow(draw_fig)

该软件包适用于任何matplotlib图形,并提供了在每个图形更新或放入调试器后等待的选项。

I have released a package called python-drawnow that provides functionality to let a figure update, typically called within a for loop, similar to Matlab’s drawnow.

An example usage:

from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
    # can be arbitrarily complex; just to draw a figure
    #figure() # don't call!
    plot(t, x)
    #show() # don't call!

N = 1e3
figure() # call here instead!
ion()    # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
    x = sin(2 * pi * i**2 * t / 100.0)
    drawnow(draw_fig)

This package works with any matplotlib figure and provides options to wait after each figure update or drop into the debugger.


回答 5

以上所有内容可能都是正确的,但是对我来说,“在线更新”数字仅适用于某些后端wx。您可能只是想尝试更改为该值,例如通过启动ipython / pylab ipython --pylab=wx!祝好运!

All of the above might be true, however for me “online-updating” of figures only works with some backends, specifically wx. You just might try to change to this, e.g. by starting ipython/pylab by ipython --pylab=wx! Good luck!


回答 6

这对我有用:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
    clear_output(wait=True)
    y = np.random.random([10,1])
    plt.plot(y)
    plt.show()

This worked for me:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
    clear_output(wait=True)
    y = np.random.random([10,1])
    plt.plot(y)
    plt.show()

如何在matplotlib中获得多个子图?

问题:如何在matplotlib中获得多个子图?

我对这段代码的工作方式有些困惑:

fig, axes = plt.subplots(nrows=2, ncols=2)
plt.show()

在这种情况下,无花果轴如何工作?它有什么作用?

另外,为什么这项工作不做同样的事情:

fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)

I am a little confused about how this code works:

fig, axes = plt.subplots(nrows=2, ncols=2)
plt.show()

How does the fig, axes work in this case? What does it do?

Also why wouldn’t this work to do the same thing:

fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)

回答 0

有几种方法可以做到这一点。该subplots方法将创建图形以及随后存储在ax数组中的子图。例如:

import matplotlib.pyplot as plt

x = range(10)
y = range(10)

fig, ax = plt.subplots(nrows=2, ncols=2)

for row in ax:
    for col in row:
        col.plot(x, y)

plt.show()

但是,类似的事情也可以使用,但是并不是很“干净”,因为您要创建带有子图的图形,然后在其上添加:

fig = plt.figure()

plt.subplot(2, 2, 1)
plt.plot(x, y)

plt.subplot(2, 2, 2)
plt.plot(x, y)

plt.subplot(2, 2, 3)
plt.plot(x, y)

plt.subplot(2, 2, 4)
plt.plot(x, y)

plt.show()

There are several ways to do it. The subplots method creates the figure along with the subplots that are then stored in the ax array. For example:

import matplotlib.pyplot as plt

x = range(10)
y = range(10)

fig, ax = plt.subplots(nrows=2, ncols=2)

for row in ax:
    for col in row:
        col.plot(x, y)

plt.show()

However, something like this will also work, it’s not so “clean” though since you are creating a figure with subplots and then add on top of them:

fig = plt.figure()

plt.subplot(2, 2, 1)
plt.plot(x, y)

plt.subplot(2, 2, 2)
plt.plot(x, y)

plt.subplot(2, 2, 3)
plt.plot(x, y)

plt.subplot(2, 2, 4)
plt.plot(x, y)

plt.show()


回答 1

import matplotlib.pyplot as plt

fig, ax = plt.subplots(2, 2)

ax[0, 0].plot(range(10), 'r') #row=0, col=0
ax[1, 0].plot(range(10), 'b') #row=1, col=0
ax[0, 1].plot(range(10), 'g') #row=0, col=1
ax[1, 1].plot(range(10), 'k') #row=1, col=1
plt.show()

import matplotlib.pyplot as plt

fig, ax = plt.subplots(2, 2)

ax[0, 0].plot(range(10), 'r') #row=0, col=0
ax[1, 0].plot(range(10), 'b') #row=1, col=0
ax[0, 1].plot(range(10), 'g') #row=0, col=1
ax[1, 1].plot(range(10), 'k') #row=1, col=1
plt.show()


回答 2

  • 您也可以在子图调用中打开轴的包装

  • 并设置是否要在子图之间共享x和y轴

像这样:

import matplotlib.pyplot as plt
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
ax1.plot(range(10), 'r')
ax2.plot(range(10), 'b')
ax3.plot(range(10), 'g')
ax4.plot(range(10), 'k')
plt.show()

  • You can also unpack the axes in the subplots call

  • And set whether you want to share the x and y axes between the subplots

Like this:

import matplotlib.pyplot as plt
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
ax1.plot(range(10), 'r')
ax2.plot(range(10), 'b')
ax3.plot(range(10), 'g')
ax4.plot(range(10), 'k')
plt.show()


回答 3

您可能对以下事实感兴趣:从matplotlib 2.1版开始,问题的第二个代码也很好用。

更改日志

Figure类现在具有subplots方法Figure类现在具有subplots()方法,该方法的行为与pyplot.subplots()相同,但是在现有的图形上。

例:

import matplotlib.pyplot as plt

fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)

plt.show()

You might be interested in the fact that as of matplotlib version 2.1 the second code from the question works fine as well.

From the change log:

Figure class now has subplots method The Figure class now has a subplots() method which behaves the same as pyplot.subplots() but on an existing figure.

Example:

import matplotlib.pyplot as plt

fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)

plt.show()

回答 4

阅读文档:matplotlib.pyplot.subplots

pyplot.subplots()返回一个fig, ax用符号解压缩为两个变量的元组

fig, axes = plt.subplots(nrows=2, ncols=2)

代码

fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)

不起作用,因为subplots()pyplot不是对象成员的函数Figure

read the documentation: matplotlib.pyplot.subplots

pyplot.subplots() returns a tuple fig, ax which is unpacked in two variables using the notation

fig, axes = plt.subplots(nrows=2, ncols=2)

the code

fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)

does not work because subplots()is a function in pyplot not a member of the object Figure.


在matplotlib中设置颜色栏范围

问题:在matplotlib中设置颜色栏范围

我有以下代码:

import matplotlib.pyplot as plt

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

plt.clf()
plt.pcolor(X, Y, v, cmap=cm)
plt.loglog()
plt.xlabel('X Axis')
plt.ylabel('Y Axis')

plt.colorbar()
plt.show()

因此,这将使用指定的颜色图在X轴和Y轴上生成值“ v”的图形。X和Y轴是完美的,但是颜色图在v的最小值和最大值之间分布。我想强制颜色图的范围在0到1之间。

我想到使用:

plt.axis(...)

设置轴的范围,但这仅接受X和Y的最小值和最大值的参数,而不使用颜色图。

编辑:

为了清楚起见,假设我有一个图的值的范围为(0 … 0.3),而另一个图的值为(0.2 … 0.8)。

在两个图中,我都希望颜色条的范围为(0 … 1)。在两个图中,我希望使用上述整个cdict范围时该颜色范围是相同的(因此,两个图中的0.25将是相同颜色)。在第一个图形中,介于0.3到1.0之间的所有颜色将不会显示在图形中,但是会在侧面的颜色栏键中显示。另一方面,所有介于0和0.2之间以及介于0.8和1之间的颜色都不会出现在图表中,而是会出现在侧面的颜色栏中。

I have the following code:

import matplotlib.pyplot as plt

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

plt.clf()
plt.pcolor(X, Y, v, cmap=cm)
plt.loglog()
plt.xlabel('X Axis')
plt.ylabel('Y Axis')

plt.colorbar()
plt.show()

So this produces a graph of the values ‘v’ on the axes X vs Y, using the specified colormap. The X and Y axes are perfect, but the colormap spreads between the min and max of v. I would like to force the colormap to range between 0 and 1.

I thought of using:

plt.axis(...)

To set the ranges of the axes, but this only takes arguments for the min and max of X and Y, not the colormap.

Edit:

For clarity, let’s say I have one graph whose values range (0 … 0.3), and another graph whose values (0.2 … 0.8).

In both graphs, I will want the range of the colorbar to be (0 … 1). In both graphs, I want this range of colour to be identical using the full range of cdict above (so 0.25 in both graphs will be the same colour). In the first graph, all colours between 0.3 and 1.0 won’t feature in the graph, but will in the colourbar key at the side. In the other, all colours between 0 and 0.2, and between 0.8 and 1 will not feature in the graph, but will in the colourbar at the side.


回答 0

使用vminvmax强制使用颜色范围。这是一个例子:

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )

def do_plot(n, f, title):
    #plt.clf()
    plt.subplot(1, 3, n)
    plt.pcolor(X, Y, f(data), cmap=cm, vmin=-4, vmax=4)
    plt.title(title)
    plt.colorbar()

plt.figure()
do_plot(1, lambda x:x, "all")
do_plot(2, lambda x:np.clip(x, -4, 0), "<0")
do_plot(3, lambda x:np.clip(x, 0, 4), ">0")
plt.show()

Using vmin and vmax forces the range for the colors. Here’s an example:

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )

def do_plot(n, f, title):
    #plt.clf()
    plt.subplot(1, 3, n)
    plt.pcolor(X, Y, f(data), cmap=cm, vmin=-4, vmax=4)
    plt.title(title)
    plt.colorbar()

plt.figure()
do_plot(1, lambda x:x, "all")
do_plot(2, lambda x:np.clip(x, -4, 0), "<0")
do_plot(3, lambda x:np.clip(x, 0, 4), ">0")
plt.show()

回答 1

使用CLIM函数(相当于MATLAB中的CAXIS函数):

plt.pcolor(X, Y, v, cmap=cm)
plt.clim(-4,4)  # identical to caxis([-4,4]) in MATLAB
plt.show()

Use the CLIM function (equivalent to CAXIS function in MATLAB):

plt.pcolor(X, Y, v, cmap=cm)
plt.clim(-4,4)  # identical to caxis([-4,4]) in MATLAB
plt.show()

回答 2

不知道这是否是最优雅的解决方案(这就是我使用的解决方案),但是您可以将数据缩放到0到1之间的范围,然后修改颜色栏:

import matplotlib as mpl
...
ax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.5)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=cm,
                       norm=mpl.colors.Normalize(vmin=-0.5, vmax=1.5))
cbar.set_clim(-2.0, 2.0)

使用两个不同的限制,您可以控制颜色栏的范围和图例。在此示例中,栏中仅显示-0.5到1.5之间的范围,而色图则覆盖-2到2(因此这可能是您的数据范围,您在缩放之前记录了该范围)。

因此,您不必缩放颜色图,而是可以缩放数据并使颜色条适合该值。

Not sure if this is the most elegant solution (this is what I used), but you could scale your data to the range between 0 to 1 and then modify the colorbar:

import matplotlib as mpl
...
ax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.5)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=cm,
                       norm=mpl.colors.Normalize(vmin=-0.5, vmax=1.5))
cbar.set_clim(-2.0, 2.0)

With the two different limits you can control the range and legend of the colorbar. In this example only the range between -0.5 to 1.5 is show in the bar, while the colormap covers -2 to 2 (so this could be your data range, which you record before the scaling).

So instead of scaling the colormap you scale your data and fit the colorbar to that.


回答 3

使用图形环境和.set_clim()

如果您有多个图,可能会更容易,更安全地进行此选择:

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )
data1 = np.clip(data,0,6)
data2 = np.clip(data,-6,0)
vmin = np.min(np.array([data,data1,data2]))
vmax = np.max(np.array([data,data1,data2]))

fig = plt.figure()
ax = fig.add_subplot(131)
mesh = ax.pcolormesh(data, cmap = cm)
mesh.set_clim(vmin,vmax)
ax1 = fig.add_subplot(132)
mesh1 = ax1.pcolormesh(data1, cmap = cm)
mesh1.set_clim(vmin,vmax)
ax2 = fig.add_subplot(133)
mesh2 = ax2.pcolormesh(data2, cmap = cm)
mesh2.set_clim(vmin,vmax)
# Visualizing colorbar part -start
fig.colorbar(mesh,ax=ax)
fig.colorbar(mesh1,ax=ax1)
fig.colorbar(mesh2,ax=ax2)
fig.tight_layout()
# Visualizing colorbar part -end

plt.show()

单个彩条

最好的选择是对整个图使用单个颜色条。有多种方法可以完成此操作,教程对于了解最佳选择非常有用。我更喜欢这种解决方案,您只需复制和粘贴即可,而不是之前的可视化颜色栏代码。

fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
                    wspace=0.4, hspace=0.1)
cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
cbar = fig.colorbar(mesh, cax=cb_ax)

聚苯乙烯

我建议使用pcolormesh代替,pcolor因为它速度更快(此处有更多信息)。

Using figure environment and .set_clim()

Could be easier and safer this alternative if you have multiple plots:

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )
data1 = np.clip(data,0,6)
data2 = np.clip(data,-6,0)
vmin = np.min(np.array([data,data1,data2]))
vmax = np.max(np.array([data,data1,data2]))

fig = plt.figure()
ax = fig.add_subplot(131)
mesh = ax.pcolormesh(data, cmap = cm)
mesh.set_clim(vmin,vmax)
ax1 = fig.add_subplot(132)
mesh1 = ax1.pcolormesh(data1, cmap = cm)
mesh1.set_clim(vmin,vmax)
ax2 = fig.add_subplot(133)
mesh2 = ax2.pcolormesh(data2, cmap = cm)
mesh2.set_clim(vmin,vmax)
# Visualizing colorbar part -start
fig.colorbar(mesh,ax=ax)
fig.colorbar(mesh1,ax=ax1)
fig.colorbar(mesh2,ax=ax2)
fig.tight_layout()
# Visualizing colorbar part -end

plt.show()

A single colorbar

The best alternative is then to use a single color bar for the entire plot. There are different ways to do that, this tutorial is very useful for understanding the best option. I prefer this solution that you can simply copy and paste instead of the previous visualizing colorbar part of the code.

fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
                    wspace=0.4, hspace=0.1)
cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
cbar = fig.colorbar(mesh, cax=cb_ax)

P.S.

I would suggest using pcolormesh instead of pcolor because it is faster (more infos here ).


Matplotlib散点图; 颜色作为第三个变量的函数

问题:Matplotlib散点图; 颜色作为第三个变量的函数

我想制作一个散点图(使用matplotlib),其中根据第三个变量对点进行阴影处理。我对此非常了解:

plt.scatter(w, M, c=p, marker='s')

其中w和M是数据点,而p是我要针对其着色的变量。
但是我想用灰度而不是彩色来做。有人可以帮忙吗?

I want to make a scatterplot (using matplotlib) where the points are shaded according to a third variable. I’ve got very close with this:

plt.scatter(w, M, c=p, marker='s')

where w and M are the datapoints and p is the variable I want to shade with respect to.
However I want to do it in greyscale rather than colour. Can anyone help?


回答 0

无需手动设置颜色。相反,请指定灰度颜色图…

import numpy as np
import matplotlib.pyplot as plt

# Generate data...
x = np.random.random(10)
y = np.random.random(10)

# Plot...
plt.scatter(x, y, c=y, s=500)
plt.gray()

plt.show()

或者,如果您希望使用更大范围的颜色图,也可以将cmapkwarg 指定为scatter。要使用其中任何一个的反向版本,只需指定其中_r任何一个的“ ”版本即可。例如,gray_r而不是gray。有几种不同的灰度色彩映射预先制作的(如graygist_yargbinary,等)。

import matplotlib.pyplot as plt
import numpy as np

# Generate data...
x = np.random.random(10)
y = np.random.random(10)

plt.scatter(x, y, c=y, s=500, cmap='gray')
plt.show()

There’s no need to manually set the colors. Instead, specify a grayscale colormap…

import numpy as np
import matplotlib.pyplot as plt

# Generate data...
x = np.random.random(10)
y = np.random.random(10)

# Plot...
plt.scatter(x, y, c=y, s=500)
plt.gray()

plt.show()

Or, if you’d prefer a wider range of colormaps, you can also specify the cmap kwarg to scatter. To use the reversed version of any of these, just specify the “_r” version of any of them. E.g. gray_r instead of gray. There are several different grayscale colormaps pre-made (e.g. gray, gist_yarg, binary, etc).

import matplotlib.pyplot as plt
import numpy as np

# Generate data...
x = np.random.random(10)
y = np.random.random(10)

plt.scatter(x, y, c=y, s=500, cmap='gray')
plt.show()

回答 1

在matplotlib中,可以将灰色表示为介于0-1之间的数字值。
例如c = '0.1'

然后,您可以将第三个变量转换为该范围内的值,并使用它为点着色。
在以下示例中,我将点的y位置用作确定颜色的值:

from matplotlib import pyplot as plt

x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
y = [125, 32, 54, 253, 67, 87, 233, 56, 67]

color = [str(item/255.) for item in y]

plt.scatter(x, y, s=500, c=color)

plt.show()

In matplotlib grey colors can be given as a string of a numerical value between 0-1.
For example c = '0.1'

Then you can convert your third variable in a value inside this range and to use it to color your points.
In the following example I used the y position of the point as the value that determines the color:

from matplotlib import pyplot as plt

x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
y = [125, 32, 54, 253, 67, 87, 233, 56, 67]

color = [str(item/255.) for item in y]

plt.scatter(x, y, s=500, c=color)

plt.show()


回答 2

有时您可能需要根据x值情况精确绘制颜色。例如,您可能有一个包含3种类型的变量和一些数据点的数据框。您想做以下事情,

  • 在RED中绘制对应于物理变量’A’的点。
  • 在BLUE中绘制与物理变量“ B”相对应的点。
  • 在绿色中绘制对应于物理变量“ C”的点。

在这种情况下,您可能必须编写short函数以将x值映射为对应的颜色名称作为列表,然后将该列表传递给plt.scatter命令。

x=['A','B','B','C','A','B']
y=[15,30,25,18,22,13]

# Function to map the colors as a list from the input list of x variables
def pltcolor(lst):
    cols=[]
    for l in lst:
        if l=='A':
            cols.append('red')
        elif l=='B':
            cols.append('blue')
        else:
            cols.append('green')
    return cols
# Create the colors list using the function above
cols=pltcolor(x)

plt.scatter(x=x,y=y,s=500,c=cols) #Pass on the list created by the function here
plt.grid(True)
plt.show()

Sometimes you may need to plot color precisely based on the x-value case. For example, you may have a dataframe with 3 types of variables and some data points. And you want to do following,

  • Plot points corresponding to Physical variable ‘A’ in RED.
  • Plot points corresponding to Physical variable ‘B’ in BLUE.
  • Plot points corresponding to Physical variable ‘C’ in GREEN.

In this case, you may have to write to short function to map the x-values to corresponding color names as a list and then pass on that list to the plt.scatter command.

x=['A','B','B','C','A','B']
y=[15,30,25,18,22,13]

# Function to map the colors as a list from the input list of x variables
def pltcolor(lst):
    cols=[]
    for l in lst:
        if l=='A':
            cols.append('red')
        elif l=='B':
            cols.append('blue')
        else:
            cols.append('green')
    return cols
# Create the colors list using the function above
cols=pltcolor(x)

plt.scatter(x=x,y=y,s=500,c=cols) #Pass on the list created by the function here
plt.grid(True)
plt.show()


指定并保存具有精确大小(以像素为单位)的图形

问题:指定并保存具有精确大小(以像素为单位)的图形

假设我的图像尺寸为3841 x 7195像素。我想将图形的内容保存到磁盘,以得到我指定的确切大小的图像(以像素为单位)。

没有轴,没有标题。只是图像。我个人并不关心DPI,因为我只想以像素为单位指定图像在屏幕上所占的大小。

我已经阅读了其他 线程,它们似乎都将转换为英寸,然后以英寸为单位指定图形的尺寸,并以某种方式调整dpi。我想避免处理像素到英寸转换可能导致的精度损失。

我尝试过:

w = 7195
h = 3841
fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig(some_path, dpi=1)

没有运气(Python抱怨宽度和高度都必须低于32768(?))

从我所看到的一切来看,都matplotlib需要在inches和中指定图形大小dpi,但是我只对图形在磁盘中占据的像素感兴趣。我怎样才能做到这一点?

需要说明的是:我正在寻找一种使用matplotlib而不是其他图像保存库的方法。

Say I have an image of size 3841 x 7195 pixels. I would like to save the contents of the figure to disk, resulting in an image of the exact size I specify in pixels.

No axis, no titles. Just the image. I don’t personally care about DPIs, as I only want to specify the size the image takes in the screen in disk in pixels.

I have read other threads, and they all seem to do conversions to inches and then specify the dimensions of the figure in inches and adjust dpi’s in some way. I would like to avoid dealing with the potential loss of accuracy that could result from pixel-to-inches conversions.

I have tried with:

w = 7195
h = 3841
fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig(some_path, dpi=1)

with no luck (Python complains that width and height must each be below 32768 (?))

From everything I have seen, matplotlib requires the figure size to be specified in inches and dpi, but I am only interested in the pixels the figure takes in disk. How can I do this?

To clarify: I am looking for a way to do this with matplotlib, and not with other image-saving libraries.


回答 0

Matplotlib不能直接处理像素,而是可以处理物理尺寸和DPI。如果要显示具有特定像素大小的图形,则需要知道显示器的DPI。例如,此链接将为您检测到该链接

如果您具有3841×7195像素的图像,则监视器不太可能会那么大,因此您将无法显示该尺寸的图形(matplotlib要求该图形适合屏幕尺寸,如果您要求一个尺寸太大会缩小到屏幕尺寸)。让我们想象一下,您仅需要一个800×800像素的图像作为示例。这是在监视器(my_dpi=96)中显示800×800像素图像的方法:

plt.figure(figsize=(800/my_dpi, 800/my_dpi), dpi=my_dpi)

因此,您基本上只需将尺寸(以英寸为单位)除以DPI。

如果要保存特定大小的图形,则是另一回事。屏幕DPI不再那么重要了(除非您要求提供一个不适合屏幕的数字)。使用相同的800×800像素图形示例,我们可以使用dpi关键字来将其保存为不同的分辨率savefig。要将其保存为与屏幕相同的分辨率,只需使用相同的dpi:

plt.savefig('my_fig.png', dpi=my_dpi)

要将其保存为8000×8000像素的图像,请使用10倍大的dpi:

plt.savefig('my_fig.png', dpi=my_dpi * 10)

请注意,并非所有后端都支持DPI的设置。在这里,使用PNG后端,但是pdf和ps后端将以不同的方式实现大小。同样,更改DPI和大小也会影响诸如fontsize之类的内容。较大的DPI将保持相同的字体和元素相对大小,但是如果您希望较小的字体用于较大的图形,则需要增加物理尺寸而不是DPI。

回到您的示例,如果要保存3841 x 7195像素的图像,可以执行以下操作:

plt.figure(figsize=(3.841, 7.195), dpi=100)
( your code ...)
plt.savefig('myfig.png', dpi=1000)

请注意,我使用的数字dpi为100以适合大多数屏幕,但dpi=1000为了获得所需的分辨率而将其保存下来。在我的系统中,这会生成一个3840×7190像素的png -似乎保存的DPI总是比所选值小0.02像素/英寸,这将对大图像尺寸产生(较小)影响。这里对此进行更多讨论。

Matplotlib doesn’t work with pixels directly, but rather physical sizes and DPI. If you want to display a figure with a certain pixel size, you need to know the DPI of your monitor. For example this link will detect that for you.

If you have an image of 3841×7195 pixels it is unlikely that you monitor will be that large, so you won’t be able to show a figure of that size (matplotlib requires the figure to fit in the screen, if you ask for a size too large it will shrink to the screen size). Let’s imagine you want an 800×800 pixel image just for an example. Here’s how to show an 800×800 pixel image in my monitor (my_dpi=96):

plt.figure(figsize=(800/my_dpi, 800/my_dpi), dpi=my_dpi)

So you basically just divide the dimensions in inches by your DPI.

If you want to save a figure of a specific size, then it is a different matter. Screen DPIs are not so important anymore (unless you ask for a figure that won’t fit in the screen). Using the same example of the 800×800 pixel figure, we can save it in different resolutions using the dpi keyword of savefig. To save it in the same resolution as the screen just use the same dpi:

plt.savefig('my_fig.png', dpi=my_dpi)

To to save it as an 8000×8000 pixel image, use a dpi 10 times larger:

plt.savefig('my_fig.png', dpi=my_dpi * 10)

Note that the setting of the DPI is not supported by all backends. Here, the PNG backend is used, but the pdf and ps backends will implement the size differently. Also, changing the DPI and sizes will also affect things like fontsize. A larger DPI will keep the same relative sizes of fonts and elements, but if you want smaller fonts for a larger figure you need to increase the physical size instead of the DPI.

Getting back to your example, if you want to save a image with 3841 x 7195 pixels, you could do the following:

plt.figure(figsize=(3.841, 7.195), dpi=100)
( your code ...)
plt.savefig('myfig.png', dpi=1000)

Note that I used the figure dpi of 100 to fit in most screens, but saved with dpi=1000 to achieve the required resolution. In my system this produces a png with 3840×7190 pixels — it seems that the DPI saved is always 0.02 pixels/inch smaller than the selected value, which will have a (small) effect on large image sizes. Some more discussion of this here.


回答 1

根据您的代码,这对我有用,生成了一个93Mb png图像,带有彩色噪声和所需的尺寸:

import matplotlib.pyplot as plt
import numpy

w = 7195
h = 3841

im_np = numpy.random.rand(h, w)

fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig('figure.png', dpi=1)

我正在使用Linux Mint 13中Python 2.7库的最新PIP版本。

希望有帮助!

This worked for me, based on your code, generating a 93Mb png image with color noise and the desired dimensions:

import matplotlib.pyplot as plt
import numpy

w = 7195
h = 3841

im_np = numpy.random.rand(h, w)

fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig('figure.png', dpi=1)

I am using the last PIP versions of the Python 2.7 libraries in Linux Mint 13.

Hope that helps!


回答 2

根据tiago接受的响应,这是一个小型通用函数,该函数将numpy数组导出到与该数组具有相同分辨率的图像:

import matplotlib.pyplot as plt
import numpy as np

def export_figure_matplotlib(arr, f_name, dpi=200, resize_fact=1, plt_show=False):
    """
    Export array as figure in original resolution
    :param arr: array of image to save in original resolution
    :param f_name: name of file where to save figure
    :param resize_fact: resize facter wrt shape of arr, in (0, np.infty)
    :param dpi: dpi of your screen
    :param plt_show: show plot or not
    """
    fig = plt.figure(frameon=False)
    fig.set_size_inches(arr.shape[1]/dpi, arr.shape[0]/dpi)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(arr)
    plt.savefig(f_name, dpi=(dpi * resize_fact))
    if plt_show:
        plt.show()
    else:
        plt.close()

如tiago上次答复中所述,需要首先找到屏幕DPI,例如,可以在此处完成:http : //dpi.lv

resize_fact在函数中添加了一个附加参数,例如,您可以将图像导出到原始分辨率的50%(0.5)。

Based on the accepted response by tiago, here is a small generic function that exports a numpy array to an image having the same resolution as the array:

import matplotlib.pyplot as plt
import numpy as np

def export_figure_matplotlib(arr, f_name, dpi=200, resize_fact=1, plt_show=False):
    """
    Export array as figure in original resolution
    :param arr: array of image to save in original resolution
    :param f_name: name of file where to save figure
    :param resize_fact: resize facter wrt shape of arr, in (0, np.infty)
    :param dpi: dpi of your screen
    :param plt_show: show plot or not
    """
    fig = plt.figure(frameon=False)
    fig.set_size_inches(arr.shape[1]/dpi, arr.shape[0]/dpi)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(arr)
    plt.savefig(f_name, dpi=(dpi * resize_fact))
    if plt_show:
        plt.show()
    else:
        plt.close()

As said in the previous reply by tiago, the screen DPI needs to be found first, which can be done here for instance: http://dpi.lv

I’ve added an additional argument resize_fact in the function which which you can export the image to 50% (0.5) of the original resolution, for instance.


回答 3

plt.imsave为我工作。您可以在此处找到文档:https : //matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.imsave.html

#file_path = directory address where the image will be stored along with file name and extension
#array = variable where the image is stored. I think for the original post this variable is im_np
plt.imsave(file_path, array)

plt.imsave worked for me. You can find the documentation here: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.imsave.html

#file_path = directory address where the image will be stored along with file name and extension
#array = variable where the image is stored. I think for the original post this variable is im_np
plt.imsave(file_path, array)