使用 Python 科学绘图

使用 matplotlib 绘图

读取数据

import pandas as pd
data = pd.read_csv("fileName", sep='\t', skiprows=2, names=['x','y','z','T'])

默认逗号为分隔符,也可以设为 \t等。
默认自动将第一行作为 index,可以这样调用:data['x']
也可以手动设置 index:names=['x','y','z','T']
通过 skiprows 设置需要忽略的行:

  • 数字指定:skiprows = 2 表示忽略前 2 行。
  • 列表指定:skiprows = [0] 表示忽略第 0 行。
  • 列表指定:skiprows = range(1, 10)) 表示忽略第 1 到 9 行,保留第 0 行,和第 10 行及以后的行。

图型

点线图 plot

plot(Z, Y, color='green', linestyle='dashed', linewidth=2, marker='o', markersize=12)

官方链接:
colors
markers
Linestyles
colormaps

Base Colors

常见 linestyle

unfilled markers
unfilled-markers
filled markers

空心 markers

plt.plot(X, Y, 'o', markerfacecolor='none')

可以将 plot 中的线宽设为 0,这样只剩下 symbol:

plot(X, Y, marker='.', linewidth=0)

竖直线和水平线

水平线

ax.axhline(y=0, xmin=0, xmax=1, color='black', linewidth=2)
ax.axhline(0, color='black', linewidth=2) # y=0

竖直线

ax.axvline(x=0, ymin=0, ymax=1, color="r")
ax.axvline(0, color="r") # x=0

散点图 scatter

plt.scatter(X, Y, c=Z, s=1, marker='s', cmap=plt.cm.hot)

散点用 X Y 表示,用 Z 着色,色带是 plt.cm.hot,size是 1。
更多设置参考网址

如果要对散点着色的话,建议先将所有点按照 Z 排序:

idx = Z.argsort()
X, Y, Z = X[idx], Y[idx], Z[idx]

绘制界限分明的 colorbar

官方链接

记得选用 diverging 色条,比如官方的 seismic

divnorm = colors.TwoSlopeNorm(vcenter=0)
plt.scatter(X, Y, c=Z, norm=divnorm,cmap=matplotlib.cm.seismic)

云图 contourf

data = pd.read_csv("fileName", sep='\t')
X = data['X']
Y = data['Y']
Z = data['Z']

xi=np.linspace(min(X),max(X),1000)
yi=np.linspace(min(Y),max(Y),1000)
[XX,YY]=np.meshgrid(xi,yi)
ZZ=griddata((X,Y), Z, (XX,YY), method='cubic')


plt.contourf(XX, YY, ZZ, 20, cmap=plt.cm.hot) # 20 表示 20 个 levels
cbar = plt.colorbar()

还有一种更简便的方法:

plt.tricontourf(X, Y, Z, 20, cmap=plt.cm.hot, extend='both')

越界时指定颜色,其中,extend : {'neither', 'both', 'min', 'max'}

levels=np.arange(-10,4200000,1000)
cp=plt.contourf(XX, YY, ZZ, levels=levels, cmap=plt.cm.hot, extend='both')
cp.cmap.set_under('yellow')
cp.cmap.set_over('cyan') #注意顺序,此行须在 plt.colorbar 之前
cbar = plt.colorbar(cp)
cbar.ax.set_title('T')

小于某个数时颜色全部为空白:

levels=np.arange(0.0015,0.7515,0.0015)
plt.contourf(XX, YY, ZZ,levels=levels)#这里不能使用extend,如果使用的话,那么小于下限时,颜色直接是 min 的颜色。

设置图中的数值上下限:

设置colorbar中的上下限:

等值线

plt.contourf(XX, YY, ZZ, 20, cmap=plt.cm.hot) #这是云图,20 表示分为 20 道等值线
plt.contour(XX, YY, ZZ, 1, levels=[1000], linewidths=2, colors='y') #这是等值线,1表示一条等值线,levels里边是这个值

配件

图片颜色

自带 调色

colorMap = plt.cm.get_cmap('winter')
colorMap=plt.get_cmap("YlOrRd")
colorMap=pyplot.cm.jet

palettable 调色

源码:https://github.com/jiffyclub/palettable

from palettable.mycarta import Cube1_20
from palettable.colorbrewer.sequential import Greys_9
colorMap=Cube1_20.mpl_colormap
colorMap=Greys_9.mpl_colormap

colorcet 调色

源码:https://github.com/pyviz/colorcet

import colorcet as cc
colorMap=cc.cm["blues"]
colorMap=cc.cm["fire"]

CMasher 调色

反转颜色

自带的反转就是在后边加一个 _r,比如:colorMap=plt.cm.hot_r

反转非自带的颜色:

def reverse_colourmap(cmap, name = 'my_cmap_r'):
"""
In:
cmap, name
Out:
my_cmap_r

Explanation:
t[0] goes from 0 to 1
row i: x y0 y1 -> t[0] t[1] t[2]
/
/
row i+1: x y0 y1 -> t[n] t[1] t[2]

so the inverse should do the same:
row i+1: x y1 y0 -> 1-t[0] t[2] t[1]
/
/
row i: x y1 y0 -> 1-t[n] t[2] t[1]
"""
reverse = []
k = []

for key in cmap._segmentdata:
k.append(key)
channel = cmap._segmentdata[key]
data = []

for t in channel:
data.append((1-t[0],t[2],t[1]))
reverse.append(sorted(data))

LinearL = dict(zip(k,reverse))
my_cmap_r = mpl.colors.LinearSegmentedColormap(name, LinearL)
return my_cmap_r


cmap=cc.cm["fire"]
cmap_r = reverse_colourmap(cmap)

colorbar

官方 API

API
API2

简单使用

sc = plt.scatter(X, Y, c=Z, s=1, cmap=pyplot.cm.jet)
plt.colorbar(sc)

位置

偏移使用 pad,尺寸缩小使用 shrink

plt.colorbar(sc, pad = 0.07, shrink=0.8)

坐标控制的位置

cbar_ax = fig.add_axes([0.19, 0.06, 0.7, 0.015]) #left,bottom,left_length,bottom_length
cbar=plt.colorbar(sc, cax=cbar_ax, orientation='horizontal')

set_ticks

cbar_ticks = np.linspace(0., 5e-3, num=3, endpoint=True)
cbar.set_ticks(cbar_ticks)

怎么才能在坐标上显示 max min?

科学计数法

cbar.formatter.set_powerlimits((-1, 1))
cbar.update_ticks()

这样小于 10^(-1),或者大于 10^(1) 的值就会用科学计数法。

ticks 朝向

如果 colorbar 竖直,ticks 默认是朝右的,如果 colorbar 水平,ticks 默认朝下的。

可以这样更改:

cbar.ax.xaxis.set_ticks_position('top')#colorbar水平,horizontal
cbar.ax.yaxis.set_ticks_position('left')#colorbar竖直,vertical

也可以这样:

cbar.ax.yaxis.tick_left()

添加变量名 set_title

set_title
set_title 默认在最上方,不管 colorbar 是水平还是竖直。
有个参数 pad 表示向上偏移距离,但是 pad 等于负数时无效。

cbar.ax.set_title('$\Omega\ [-]$', pad=10)

添加变量名 set_label

推荐使用 set_label

cbar.set_label('$\Omega\ [-]$')
cbar.set_label(r"$\Delta$"+varName, va='bottom', ha='right', labelpad=10)

变量名改变位置

colorbar 是水平的,这里的 bottom 表示变量名放在水平 colorbar 的下边,labelpad 是向下的偏移距离。

cbar.set_label('T',verticalalignment='bottom', labelpad=-10)

Vertical alignment must be one of (‘top’, ‘bottom’, ‘center’, ‘baseline’)
Horizontal alignment must be one of (‘center’, ‘right’, ‘left’)

对于水平 colorbar,horizontalalignment 不管用?

图例 legend

plt.plot(X, Y, label="sim") # 这里的 label 即图例
plt.legend(loc='upper left') # 图例位置
plt.legend(loc='best') # 自动选择最合适的位置
plt.plot(legend=None)

上下左右:upper lower left right

在循环里边画很多条线时,只想保留一个 legend,循环结束后:

from collections import OrderedDict
handles, labels = plt.gca().get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys())

缩放图例中的 marker 大小: markerscale
Default is None, which will take the value from rcParams[“legend.markerscale”] = 1.0.

plt.legend(loc='upper right',markerscale=3,fancybox=True, framealpha=0.1)

缩放图例中的线宽

leg = ax.legend()

for line in leg.get_lines():
line.set_linewidth(4.0)

关于位置的高级用法:

ax1.legend(loc='upper right', bbox_to_anchor=(1.05, 4.3), ncol=3, fontsize=10)

bbox_to_anchor 表示 right 和 upper 的位置。
upper right 和 loc 里的一致,不过顺序颠倒了,这里表示图例的 right 位于主图的 1.05,图例的 upper 位于主图的 4.3。
这个位置是0-1,超过1表示坐标轴以外。
ncol=3 表示三栏

保存为图片后,如果图例超出了整个图片的范围,则可以这样操作:

lgd =plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.savefig("fig.png", bbox_extra_artists=(lgd,), bbox_inches='tight')

如果图例分了多列,columnspacing=2.0 控制列之间的间隔。
handletextpad=0.8 可以控制 marker 和 label 之间的间隔。
参考资料
参考图片

坐标轴

设置坐标轴 label

plt.xlabel('r [mm]',fontsize=12)

调整坐标轴范围

plt.axis([0, 4, 0, 4])   # 改变取值范围,[xmin, xmax, ymin, ymax]
plt.axis(xmin =0, xmax = 0.5, ymin = 0, ymax = 0.55)
plt.xlim(0,0.35)
plt.ylim(0,0.2)

自定义刻度

x_new=np.array([5,10,15,20])
plt.xticks(x_new)

刻度朝内

plt.tick_params(direction='in')

刻度小数点位数

import matplotlib.ticker as ticker
plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%.6f'))

隐藏坐标

在包含几个子图的图标中,可能需要隐藏一些重复的坐标

ax1 = plt.subplot(420+i)
plt.setp(ax1.get_xticklabels(), visible=False)
plt.setp(ax1.get_yticklabels(), visible=False)

隐藏 ticks 和 label

ax1.set_xticks([])

隐藏子刻度

plt.minorticks_off()

科学计数

plt.ticklabel_format(axis="x", style="sci", scilimits=(0,0))
ax.ticklabel_format(axis="x", style="sci", scilimits=(0,0))

x y 坐标等比例

这个用于CFD模拟场的云图,保证 x y 长度是等比例的。

ax=plt.gca()
ax.set_aspect(1.0) # set uniform length of axis

使用 LaTeX 公式

$ 包围起来并在前边加上 r 即可,最好 $ 和公式之间留一个空格。

plt.xlabel(r'Scalar dissipation rate $ [s^{-1}] $',fontsize=12)

任意位置添加文字

plt.text(1, 17, r"z=%s mm"%(str(Z)))

子图 subplot

plt.subplots_adjust(left=0.11,bottom=0.1,top=0.97,right=0.9,hspace=0.000001,wspace=0.000001)

i=0
for Z in ['2','10','30','50']:
i=i+1
ax1 = plt.subplot(410+i)
plt.plot(R_Mean,U_Mean,linestyle='--',marker='o', color='r',label="sim")

subplot(410+i) 表示子图 4x1 排列,即 4 行 1 列。此处的循环,令 i 分分别等于 1 2 3 4。

也可以用subplot(4,1,i)表示。

绘制着色的多个子图(云图或者上色=的散点图)需要注意他们的数据 min max可能不同。
可以设置 vmin vmax是他们保持一致:

plt.scatter(x1, y1, c=z1, s=2, marker='o', vmin=minZ, vmax=maxZ)
plt.scatter(x2, y2, c=z2, s=2, marker='s', vmin=minZ, vmax=maxZ)

图片标题

plt.title('source_Yc',fontsize=12)

alpha 表示不透明度,1表示完全不透明

可以用于 plot scatter contour contourf 中。

输出图片

紧凑布局

plt.savefig("abc.png", dpi=500, bbox_inches='tight', pad_inches=0.01)
plt.savefig("abc.pdf", bbox_inches='tight', pad_inches=0.01)
plt.close()

fig = plt.figure()
fig.savefig("abc.png", dpi=500, bbox_inches='tight', pad_inches=0.01)
fig.savefig("abc.pdf", bbox_inches='tight', pad_inches=0.01)
fig.close()

python 相关

利用正则表达式获取当前目录下的某些文件

import glob
files = glob.iglob(r'0.*')
for file in files:
print file

普通版本:


files = ['0.1', '0.2']
for file in files:
print file

换行

ylabel('this is vertical\ntest', multialignment='center')#\n表示换行

灰色系列填充彩图

在图中使用其它字体

https://stackoverflow.com/a/53645383
我成功操作的经历:

把字体拷贝到:
/public/home/yao1/packages_ZY/anaconda_ZY/anaconda3/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf
然后:
rm -rf ~/.cache/matplotlib/

使用字体:

from matplotlib import rcParams
rcParams.update({'font.size': 12, 'font.family': 'Times New Roman'})

from matplotlib import rcParams
rcParams.update({'font.size': 12, 'font.family': 'simsun'})

ax1.legend(loc='upper right', bbox_to_anchor=(0.92, 3.7), ncol=3,fontsize=10,prop={'family':'simsun'})

plt.text(1.03,5.4,'平均值',fontname='simsun')

疑难解答

服务器绘图

报错 QXcbConnection: Could not connect to display

solution:
在脚本的最前边加上

import matplotlib
matplotlib.use('Agg')
文章作者: Yan Zhang
文章链接: https://openfoam.top/matplotlib/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 OpenFOAM 成长之路
您的肯定会给我更大动力~