Matplotlib pcolor绘图

3 投票
1 回答
5725 浏览
提问于 2025-04-17 12:05

我正在使用Matplotlib这个工具来根据一些数据创建一张图片。我的所有数据值都在0到1之间,我想根据这些值来给数据上色,使用的是一种叫做色图的东西。在Matlab中,这个方法效果很好,但当我把代码转到Python时,输出的结果却是一块黑色的方块。我觉得这可能是因为我绘制图片的方式不对,所以所有的数据都被当成了0。我已经花了好几个小时在网上搜索这个问题,也试过用plt.set_clim([0, 1]),但似乎没有任何效果。我对Python和Matplotlib还很陌生,虽然我之前有编程经验(比如Java、JavaScript、PHP等),但我还是看不出哪里出错了。如果有人能指出我代码中明显的错误,我会非常感激。

谢谢

from numpy import *
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.colors as myColor

e1cx=[]
e1cy=[]
e1cz=[]
print("Reading files...")
in_file = open("eigenvector_1_component_x.txt", "rt")
for line in in_file.readlines():
e1cx.append([])
for i in line.split():
    e1cx[-1].append(float(i))
in_file.close()
in_file = open("eigenvector_1_component_y.txt", "rt")
for line in in_file.readlines():
e1cy.append([])
for i in line.split():
    e1cy[-1].append(float(i))
in_file.close()
in_file = open("eigenvector_1_component_z.txt", "rt")
for line in in_file.readlines():
e1cz.append([])
for i in line.split():
    e1cz[-1].append(float(i))
in_file.close()
print("...done")

nx = 120
ny = 128
nz = 190


fx = zeros((nz,nx,ny))
fy = zeros((nz,nx,ny))
fz = zeros((nz,nx,ny))

z = 0
while z<nz-1:
x = 0
while x<nx:
    y = 0
    while y<ny:
        fx[z][x][y]=e1cx[(z*128)+y][x]
        fy[z][x][y]=e1cy[(z*128)+y][x]
        fz[z][x][y]=e1cz[(z*128)+y][x]
        y += 1
    x += 1
z+=1
if((z % 10) == 0):      
    plt.figure(num=None)
    plt.axis("off")
    normals = myColor.Normalize(vmin=0,vmax=1)
    plt.pcolor(fx[z][:][:],cmap='spectral', norm=normals)   
    filename = 'Imagex_%d' % z
    plt.savefig(filename)
    plt.colorbar(ticks=[0,2,4,6], format='%0.2f')

1 个回答

7

虽然你已经解决了最初的问题,并且有了可以工作的代码,但我想提醒你,Python和NumPy提供了很多工具,可以让这样的代码写起来简单得多。下面是一些例子:

加载数据

与其在一个空列表的末尾不断添加元素,不如直接从其他列表生成它们。例如,代替

e1cx = []
for line in in_file.readlines():
  e1cx.append([])
  for i in line.split():
    e1cx[-1].append(float(i))

你可以简单地写成:

e1cx = [[float(i) for i in line.split()] for line in in_file]

这种写法 [x(y) for y in l] 被称为 列表推导式,它不仅更简洁,而且执行速度比 for 循环更快。

不过,对于从文本文件加载表格数据,使用 numpy.loadtxt 会更简单:

import numpy as np
e1cx = np.loadtxt("eigenvector_1_component_x.txt")

想了解更多信息,

print np.loadtxt.__doc__

另外,还有一个稍微复杂一点的工具 numpy.genfromtxt

重塑数据

现在我们已经加载了数据,接下来需要重塑它。你使用的 while 循环可以正常工作,但 numpy 提供了更简单的方法。首先,如果你更喜欢用自己的方法加载数据,可以使用 e1cx = array(e1cx) 等将你的特征向量数组转换为合适的 NumPy 数组。

array 类提供了一些方法,可以重新排列数组中数据的索引,而不需要复制数据。最简单的方法是 array.reshape,它可以完成你 while 循环的一半工作:

almost_fx = e1cx.reshape((nz,ny,nx))

在这里,almost_fx 是一个三维数组,可以用 almost_fx[iz,iy,ix] 来索引。需要注意的是,e1cxalmost_fx 共享数据。所以,如果你改变了 e1cx[0,0],那么 almost_fx[0,0,0] 也会相应改变。

在你的代码中,你交换了 x 和 y 的位置。如果这正是你想要的,可以用 array.swapaxes 来实现:

fx = almost_fx.swapaxes(1,2)

当然,你也可以把这个合并成一行

fx = e1cx.reshape((nz,ny,nx)).swapaxes(1,2)

不过,如果你希望 z 切片(fx[z,:,:])以 x 为水平,y 为垂直来绘图,可能就不想交换上面的轴了。只需重塑并绘图即可。

切片数组

最后,与其在 z 索引上循环并测试是否是 10 的倍数,不如直接对数组的一个切片进行循环,使用:

for fx_slice in fx[::10]:
  # plot fx_slice and save it 

这种索引语法是 array[start:end:step],其中 start 包含在结果中,而 end 不包含。留空 start 表示从 0 开始,留空 end 表示到列表的末尾。

总结

总之,你的完整代码(在引入一些 Python 的习惯用法,比如 enumerate 后)可能看起来像这样:

import numpy as np
from matplotlib import pyplot as pt

shape = (190,128,120)

fx = np.loadtxt("eigenvectors_1_component_x.txt").reshape(shape).swapaxes(1,2)

for i,fx_slice in enumerate(fx[::10]):
  z = i*10
  pt.figure()
  pt.axis("off")
  pt.pcolor(fx_slice, cmap='spectral', vmin=0, vmax=1)
  pt.colorbar(ticks=[0,2,4,6], format='%0.2f')
  pt.savefig('Imagex_%d' % z)

另外,如果你想每个元素对应一个像素,可以用下面的内容替换 for 循环的主体:

z = i*10
pt.imsave('Imagex_%d' % z, fx_slice, cmap='spectral', vmin=0, vmax=1)

撰写回答