jbm1991
jbm1991

Reputation: 98

Matplotlib pcolor

I am using Matplotlib to create an image based on some data. All of the data falls in the range of 0 through to 1 and I am trying to color the data based on its value using a colormap and this works perfectly in Matlab, however when converting the code across to Python I simply get a black square as the output. I believe this is because I'm plotting the image wrong and so it is plotting all the data as 0. I have tried searching this problem for several hours and I have tried plt.set_clim([0, 1]) however that didn't seem to do anything. I am new to Python and Matplotlib, although I am not new to programming (Java, javascript, PHP, etc), but I cannot see where I am going wrong. If any body can see anything glaringly incorrect in my code then I would be extremely grateful.

Thank you

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')

Upvotes: 3

Views: 5758

Answers (1)

rephorm
rephorm

Reputation: 266

Although you have resolved your original issue and have code that works, I wanted to point out that both python and numpy provide several tools that make code like this much simpler to write. Here are a few examples:

Loading data

Instead of building up lists by appending to the end of an empty one, it is often easier to generate them from other lists. For example, instead of

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

you can simply write:

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

The syntax [x(y) for y in l] is known as a list comprehension, and, in addition to being more concise will execute more quickly than a for loop.

However, for loading tabular data from a text file, it is even simpler to use numpy.loadtxt:

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

for more information,

print np.loadtxt.__doc__

See also, its slightly more sophisticated cousin numpy.genfromtxt

Reshaping data

Now that we have our data loaded, we need to reshape it. The while loops you use work fine, but numpy provides an easier way. First, if you prefer to use your method of loading the data, then convert your eigenvector arrays into proper numpy arrays using e1cx = array(e1cx), etc.

The array class provides methods for rearranging how the data in an array is indexed without requiring it to be copied. The simplest method is array.reshape, which will do half of what your while loops do:

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

Here, almost_fx is a rank-3 array indexed as almost_fx[iz,iy,ix]. One important thing to be aware of is that e1cx and almost_fx share their data. So, if you change e1cx[0,0], you will also change almost_fx[0,0,0].

In your code, you swapped the x and y locations. If this is indeed what you wanted to do, you can accomplish this with array.swapaxes:

fx = almost_fx.swapaxes(1,2)

Of course, you could always combine this into one line

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

However, if you want the z-slices (fx[z,:,:]) to plot with x horizontal and y vertical, you probably do not want to swap the axes above. Just reshape and plot.

Slicing arrays

Finally, rather than looping over the z-index and testing for multiples of 10, you can loop directly over a slice of the array using:

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

This indexing syntax is array[start:end:step] where start is included in the result end is not. Leaving start blank implies 0, while leaving end blank implies the end of the list.

Summary

In summary your complete code (after introducing a few more python idioms like enumerate) could look something like:

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)

Alternatively, if you want one pixel per element, you can replace the body of the for loop with

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

Upvotes: 7

Related Questions