Python 3D isosurface from a set of 2D images - python

I am using this code:
z = np.asarray(image_list)
mydata = z[::1,::1]
fig = pl.figure(facecolor='w')
ax2 = fig.add_subplot(1,1,1,projection='3d')
x,y = np.mgrid[:mydata.shape[0],:mydata.shape[1]]
ax2.plot_surface(x,y,mydata,cmap=pl.cm.jet,rstride=1,cstride=1,linewidth=0.,antia liased=False)
ax2.set_title('3D')
ax2.set_zlim3d(0,200)
pl.show()
To plot a 3D image using a list containing a set of images, but I get this error:
Traceback (most recent call last):
ax2.plot_surface(x,y,mydata,cmap=pl.cm.jet,rstride=1,cstride=1,linewidth=0.,antialiased=False)
File "/usr/lib/pymodules/python2.7/mpl_toolkits/mplot3d/axes3d.py", line 1553, in plot_surface
X, Y, Z = np.broadcast_arrays(X, Y, Z)
File "/usr/lib/python2.7/dist-packages/numpy/lib/stride_tricks.py", line 100, in broadcast_arrays
"incompatible dimensions on axis %r." % (axis,))
ValueError: shape mismatch: two or more arrays have incompatible dimensions on axis 2
Could anyone help me with this error or suggest some other technique to create a 3D image from a image list containing 2D images?

I can't remember where I found this exactly (it was another StackOverflow thread), but this is working code - yours looks like a sample of the one I have - just change out the filename to load:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pylab as pl
from PIL import Image
import numpy as np
import pylab
img = Image.open('natureWallpaper.jpg').convert('L')
z = np.asarray(img)
#slice notation: "start:stop:step" - here it's referring to the z matrix's x and y dimensions, get the whole of each
mydata = z[::1,::1]
fig = pl.figure(facecolor='w')
# subplot(nrows, ncols, plotnumber)
ax1 = fig.add_subplot(1,2,1)
# im = ax1.imshow(mydata,interpolation='nearest',cmap=pl.cm.jet)
im = ax1.imshow(mydata,interpolation='none',cmap=pl.cm.jet)
ax1.set_title('2D')
ax2 = fig.add_subplot(1,2,2,projection='3d')
x,y = np.mgrid[:mydata.shape[0],:mydata.shape[1]] ax2.plot_surface(x,y,mydata,cmap=pl.cm.jet,rstride=10,cstride=10,linewidth=0.antialiased=False)
ax2.set_title('3D')
ax2.set_zlim3d(0,255)
pl.show()

Related

3D plot of Excel data

I'm trying to recreate this plot using some of my own excel data but I've hit a wall. So far I have:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_excel(r'/path/to/data.xlsx')
yr = df['Year']
jd = df['Jday']
dc = df['Discharge']
x = np.asarray(yr)
y = np.asarray(jd)
z = np.asarray(dc)
X,Y,Z = np.meshgrid(x,y,z)
ax = plt.figure().add_subplot(projection='3d')
ax.plot_surface(X,Y,Z, cmap='autumn')
ax.set_xlabel("Year")
ax.set_ylabel("Jday")
ax.set_zlabel("Discharge")
plt.show()
But when I run this I get:
Traceback (most recent call last):
File "/Users/Desktop/main.py", line 19, in <module>
ax.plot_surface(X,Y,Z, cmap='autumn')
File "/Users/venv/lib/python3.10/site-packages/matplotlib/_api/deprecation.py", line 412, in wrapper
return func(*inner_args, **inner_kwargs)
File "/Users/venv/lib/python3.10/site-packages/mpl_toolkits/mplot3d/axes3d.py", line 1581, in plot_surface
raise ValueError("Argument Z must be 2-dimensional.")
ValueError: Argument Z must be 2-dimensional.
Any help would be appreciated.
EDIT:
I changed my code to:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_excel(r'/path/to/data.xlsx')
yr = df['Year']
jd = df['Jday']
dc = df['Discharge']
X = np.asarray(yr).reshape(-1,2)
Y = np.asarray(jd).reshape(-1,2)
Z = np.asarray(dc).reshape(-1,2)
fig = plt.figure(figsize=(14,8))
ax = plt.axes(projection='3d')
my_cmap = plt.get_cmap('seismic')
surf = ax.plot_surface(X,Y,Z,
cmap = my_cmap,
edgecolor = 'none')
fig.colorbar(surf, ax=ax,
shrink = 0.5, aspect = 5)
plt.show()
When I run this it produces the following plot:
Which obviously doesn't match the other plot. It seems to be plotting the data from each year in a single line instead of creating filled in polygons which is what I think it's supposed to do. I have a feeling this issue has to do with the .reshape function but I'm not entirely sure.
Note: original answer completely rewritten!
The problem is, as your data stated, that the Z-argument must be two-dimensional. In your problem, you don't need np.meshgrid at all. This is typically used to make a 'grid' of all possible combinations of X/Y, after which you can use these combinations to calculate your response matrix Z. However, since all your data is read in, it is merely a reshaping of all 1d-arrays to 2d-arrays:
target_shape = (np.sqrt(X.shape[0]),-1)
X = np.reshape(X, target_shape)
Y = np.reshape(Y, target_shape)
Z = np.reshape(Z, target_shape)
Have a look at the documentation of np.reshape for some more information.

How differently do matplotlib.pyplot and seaborn treat numpy arrays?

Turns out that when trying to plot the same synthetically generated numpy arrays of 2D points (one with 12 data points and another with just 1) in both pyplot and seaborn, I have to change the array dimension of the single-point array so that the program does not yield an error:
points = np.array([[1,2],[5,6],[4,1],[3,8],[7,5],[1,5],[0,8],[4,3],[2,1],[1,7],[3,8],[2,5]])
p = np.array([2.5,2])
import matplotlib.pyplot as plt
plt.style.use("ggplot")
plt.plot(points[:,0], points[:,1],"ro")
plt.plot(p[0],p[1], "bo")
plt.axis([0,7.5,0,8.5]);
With the above code I am able to get the pyplot right. However, if I try in seaborn:
import seaborn as sns
sns.scatterplot(x = points[:,0], y = points[:,1])
sns.scatterplot(x = p[:,0], y = p[:,1])
I get the following error:
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-62-31fe2d015723> in <module>
1 import seaborn as sns
2 sns.scatterplot(x = points[:,0], y = points[:,1])
----> 3 sns.scatterplot(x = p[:,0], y = p[:,1])
IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed
Nonetheless, if I change the dimension of the p array:
p = np.array([[2.5,2]])
It now plots well in seaborn, but no longer works for plotting with pyplot. It yields the following error:
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-63-f26870ae2995> in <module>
5 plt.style.use("ggplot")
6 plt.plot(points[:,0], points[:,1],"ro")
----> 7 plt.plot(p[0],p[1], "bo")
8 plt.axis([0,7.5,0,8.5]);
IndexError: index 1 is out of bounds for axis 0 with size 1
Does anyone know why this happens?
You have an inconsequence in the way you define the point p.
You can either define p as a points array of length = 1:
p = np.array([[2.5,2]])
Then use it in pyplot as follows:
plt.plot(p[:,0],p[:,1], "bo")
and in seaborn:
sns.scatterplot(x = p[:,0], y = p[:,1])
Alternatively, if you really want to have the point p defined as a 1 dimensional array, just pass it as follows:
plt.plot(p[0], p[1], "bo")
sns.scatterplot(x = [p[0]], y = [p[1]])
Pass it like this:
sns.scatterplot(x = [p[0]], y = [p[1]])
Don't pass the values as scalar type. Pass it as a list of elements.

contour plot with mutiplile files

I have a sequence of data files which contain two columns of data (x value, and z value). I want to asign each file with a unique constant y value with a loop and then use x,y,z values to make a contour plot.
import glob
import matplotlib.pyplot as plt
import numpy as np
files=glob.glob('C:\Users\DDT\Desktop\DATA TIANYU\materials\AB2O4\synchronchron\OX1\YbFe1Mn1O4_2cyc_600_meth_ox1-*.xye')
s1=1
for file in files:
t1=s1/3
x,z = np.loadtxt(file,skiprows=3,unpack=True, usecols=[0,1])
def f(x, y):
return x*0 +y*0 +z
l1=np.size(x)
y=np.full(l1, t1,dtype=int)
X,Y=np.meshgrid(x,y)
Z = f(X,Y)
plt.contour(X,Y,Z)
s1=s1+1
continue
plt.show()
There is no error in this code, however what I got is an empty figure with nothing.
What mistake did I make?
It is very hard to guess what you're trying to do. Here is an attempt. It supposes that all x-arrays are equal. And that the y really makes sense (although that is hard if the files are read in an unspecified order). To get a useful plot, the data from all the files should be collected before starting to plot.
import glob
import matplotlib.pyplot as plt
import numpy as np
files = glob.glob('........')
zs = []
for file in files:
x, z = np.loadtxt(file, skiprows=3, unpack=True, usecols=[0, 1])
zs.append(z)
# without creating a new x, the x from the last file will be used
# x = np.linspace(0, 15, 10)
y = np.linspace(-100, 1000, len(zs))
zs = np.array(zs)
fig, axs = plt.subplots(ncols=2)
axs[0].scatter(np.tile(x, y.size), np.repeat(y, x.size), c=zs)
axs[1].contour(x, y, zs)
plt.show()
With simulated random data, the scatter plot and the contour plot would look like:

HDF5 file to diagram in python

I'm trying to generate some diagrams from an .h5 file but I don't know how to do it.
I'm using pytables, numpy and matplotlib.
The hdf5 files I use contains 2 sets of data, 2 differents curves.
My goal is to get diagrams like this one.
This is what I managed to do for the moment:
import tables as tb
import numpy as np
import matplotlib.pyplot as plt
h5file = tb.openFile(args['FILE'], "a")
for group in h5file.walkGroups("/"):
for array in h5file.walkNodes("/","Array"):
if(isinstance(array.atom.dflt, int)):
tab = np.array(array.read())
x = tab[0]
y = tab[1]
plt.plot(x, y)
plt.show()
x and y values are good but I don't know how to use them, so the result is wrong. I get a triangle instead of what I want ^^
Thank you for your help
EDIT
I solved my problem.
Here is the code :
fig = plt.figure()
tableau = np.array(array.read())
x = tableau[0]
y = tableau[1]
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.plot(x)
ax2.plot(y)
plt.title(array.name)
plt.show()

Plotting a contour plot in Python with Matplotlib with data with imshow

I am trying to plot a contour plot with Python's Matplotlib package. I'm trying to get similar results to what can be seen in this other stack overflow post. However, I'm getting the problem of it saying that there is a type error and it tells me TypeError: Invalid dimensions for image data, which can be seen in the full error code below.
Traceback (most recent call last):
File "./plot_3.py", line 68, in <module>
plt.imshow(zi, vmin=temp.min(), vmax=temp.max(), origin="lower", extent=[x.min(), x.max(), y.min(), y.max()])
File "/usr/lib64/python2.7/site-packages/matplotlib/pyplot.py", line 3022, in imshow
**kwargs)
File "/usr/lib64/python2.7/site-packages/matplotlib/__init__.py", line 1812, in inner
return func(ax, *args, **kwargs)
File "/usr/lib64/python2.7/site-packages/matplotlib/axes/_axes.py", line 4947, in imshow
im.set_data(X)
File "/usr/lib64/python2.7/site-packages/matplotlib/image.py", line 453, in set_data
raise TypeError("Invalid dimensions for image data")
TypeError: Invalid dimensions for image data
I'm unsure of what this means, as googling brought up no useful results on how to fix it. The code is listed below, and the data that I'm using can be found here. The code below simply runs the code which will parse the file and then return the data to the main where it's supposed to plot it then. To run the code, you have to use ./plot_3.py 20.0 to use it with the specific file that I posted above. x ranges from 0 to 0.3 with 61 grids, while y ranges from 0 to 0.4 with 81 grids. The data is in the format x,y,temperature where I want the temperature values to be the contour values.
from __future__ import print_function, division
import math
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import sys
import matplotlib.cm as cm
from matplotlib.mlab import griddata
import scipy.interpolate
def ParseFile(filename):
x = []
y = []
temp = []
infile = open(filename, 'r')
lines = [line.strip() for line in infile.readlines()]
for line in lines:
x.append(float(line.split(',')[0]))
y.append(float(line.split(',')[1]))
temp.append(float(line.split(',')[2]))
return np.array(x), np.array(y), np.array(temp)
time = str(sys.argv[1])
filename = time + "_sec.dat"
x,y,temp = ParseFile(filename)
xi = np.linspace(min(x), max(x))
yi = np.linspace(min(y), max(y))
zi = scipy.interpolate.griddata((x,y),temp,(xi,yi),method="linear")
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
plt.imshow(zi, vmin=temp.min(), vmax=temp.max(), origin="lower",
extent=[x.min(), x.max(), y.min(), y.max()])
plt.colorbar()
plt.show()
I think the problem is that that you need to have the points to be interpolated in a gridded format, not two 1D matrices for the interpolate.griddata function.
Adding this line to the (xi, yi) declaration I think fixes your problem:
x,y,temp = ParseFile(filename)
xi = np.linspace(min(x), max(x))
yi = np.linspace(min(y), max(y))
#create the 2D grid for interpolation:
xi, yi = np.meshgrid(xi,yi)
zi = scipy.interpolate.griddata((x,y),temp,(xi,yi),method="linear")

Categories