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

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

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.

Problem interpolating using RBF from python's scipy

I am following the accepted answer of this thread using my ownd gridded data.
I load it as:
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
import pylab as py
token = open('Ydata_48_of_50.txt','r')
linestoken=token.readlines()
tokens_column_numberX = 0
resulttokenX=[]
for x in linestoken:
resulttokenX.append(x.split()[tokens_column_numberX])
token.close()
resulttokenX = np.array(resulttokenX)
(I do the same for Y and F(X, Y)) and then, I use what is displayed in the aforementioned link:
xi, yi = np.linspace(resulttokenX.min(), resulttokenX.max(), 200), np.linspace(resulttokenY.min(), resulttokenY.max(), 200)
xi, yi = np.meshgrid(xi, yi)
# Interpolate
rbf = scipy.interpolate.Rbf(resulttokenX, resulttokenY, resulttokenF, function='linear')
Unfortunately, the last line here is an error. I get
xi, yi = np.linspace(resulttokenX2.min(), resulttokenX2.max(), 200), np.linspace(resulttokenY2.min(), resulttokenY2.max(), 200)
File "D:\Users\me\anaconda3\lib\site-packages\numpy\core\_methods.py", line 43, in _amin
return umr_minimum(a, axis, None, out, keepdims, initial, where)
TypeError: cannot perform reduce with flexible type
I have no idea why this happens, since in the original code x appears in the last line and is
type(x)
Out[26]: numpy.ndarray
which is the same type of variable as
type(resulttokenX2)
Out[24]: numpy.ndarray
I don't know why this happens. Can someone tell me what I have to do to reproduce the original code with my gridded data instead of random?
Thanks.
Edit:
resulttokenY2
Out[3]:
array(['3.2000000e+01', '3.2000000e+01',
are the first lines of resulttokenY2
After some very helpful advice from Yann ziselman I have managed to do it. This is the full code:
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
import pylab as py
import scipy
token = open('Ydata_48_of_50.txt','r')
linestoken=token.readlines()
tokens_column_numberX = 0
tokens_column_numberY = 1
tokens_column_numberF = 2
resulttokenX=[]
resulttokenY=[]
resulttokenF=[]
for x in linestoken:
resulttokenX.append(x.split()[tokens_column_numberX])
resulttokenY.append(x.split()[tokens_column_numberY])
resulttokenF.append(x.split()[tokens_column_numberF])
token.close()
resulttokenX2 = np.array(resulttokenX)
resulttokenY2 = np.array(resulttokenY)
resulttokenF2 = np.array(resulttokenF)
# Set up a regular grid of interpolation points
xi, yi = np.linspace(resulttokenX2.astype('float').min(), resulttokenX2.astype('float').max(), 100), np.linspace(resulttokenY2.astype('float').min(), resulttokenY2.astype('float').max(), 100)
xi, yi = np.meshgrid(xi, yi)
# Interpolate
rbf = scipy.interpolate.Rbf(resulttokenX2, resulttokenY2, resulttokenF2, function='linear')
zi = rbf(xi, yi)
plt.imshow(zi, vmin=resulttokenF2.astype('float').min(), vmax=resulttokenF2.astype('float').max(), origin='lower', extent=[resulttokenX2.astype('float').min(), resulttokenX2.astype('float').max(), resulttokenY2.astype('float').min(), resulttokenY2.astype('float').max()])
plt.scatter(resulttokenX2.astype('float'), resulttokenY2.astype('float'), c=resulttokenF2.astype('float'))
plt.colorbar()
plt.show()

Valid numpy arrays produces IndexError:

I am working on gfs weather data to plot certain parameters using python and matplotlib. Since it is a grib2 file, I am using wgrib2 for extracting data from file (even though I am aware of pygrib). These extracted variables (lat, lon and temp) are convered in to numpy arrary successfully. I am attaching these lat, lon and data values for your review. Portion of the code I am using for plotting is:-
lat = # load from attached lat file
lon = # load from attached lon file
data = # load data from data file
plt.figure()
m = Basemap(projection='mill', lat_ts=10, llcrnrlon=lon.min(), urcrnrlon=lon.max(), llcrnrlat=lat.min(), urcrnrlat=lat.max(),
resolution='c')
x, y = m(lat, lon)
cs = m.contourf(x, y, data)
m.drawcoastlines()
m.fillcontinents()
m.drawmapboundary()
plt.show()
Now, when I use matplotlib countorf function for plotting, it produces the following error:-
File "wgrib2.py", line 145, in <module>
cs = m.contourf(x, y, data)
File "/usr/lib/python3.6/site-packages/mpl_toolkits/basemap/__init__.py", line 521, in with_transform
return plotfunc(self,x,y,data,*args,**kwargs)
File "/usr/lib/python3.6/site-packages/mpl_toolkits/basemap/__init__.py", line 3644, in contourf
xx = x[x.shape[0]/2,:]
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
Can someone help me to solve this issue?
Here is the minimal example as requested (actual files are uploaded in above link):-
#!/usr/bin/python2
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals, division
import os
import subprocess
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
lat = np.loadtxt("lat.txt")
lon = np.loadtxt("lon.txt")
data = np.loadtxt("data.txt")
plt.figure()
m = Basemap(projection='mill', lat_ts=10, llcrnrlon=lon.min(), urcrnrlon=lon.max(), llcrnrlat=lat.min(), urcrnrlat=lat.max(),
resolution='c')
x, y = m(lat, lon)
cs = m.contourf(x, y, data)
m.drawcoastlines()
m.fillcontinents()
m.drawmapboundary()
plt.show()
Edit 1
Uploaded files again.
It appears to be an issue with dimension of the data. The output generate by wgrib2 to numpy array is a single dimension with value of (259920,) for lat, lon and data. I have checked with pygrib and it produces the shape of (720, 361) which leads to 720 x 361 = 259920. Therefore, it seems that I have an issue with the data being converted in to numpy.
Your code does not produce an error on Python 3.5 with basemap 1.1.0, so this should be fixed if you upgrade your basemap. If you are actually running Python 2 (which is suggested by your minimal example), then it may be sufficient to remove division from your __future__ imports.
In particular, this MCVE does not produce an error:
from __future__ import print_function, unicode_literals, division
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
lat = np.linspace(-90, 90, 180)
lon = np.linspace(-180, 180, 360)
lat, lon = np.meshgrid(lat, lon)
data = np.random.randn(360, 180)
plt.figure()
m = Basemap(projection='mill', lat_ts=10, llcrnrlon=lon.min(), urcrnrlon=lon.max(), llcrnrlat=lat.min(), urcrnrlat=lat.max(),
resolution='c')
x, y = m(lon, lat)
cs = m.contourf(x, y, data)
m.drawcoastlines()
m.fillcontinents()
m.drawmapboundary()
plt.show()
If for some reason that doesn't work and you want to patch the issue erroring right here, you can go into the basemap source code and change xx = x[x.shape[0]/2,:] to xx = x[int(x.shape[0]/2),:]. What I heard from Ben suggests that if you do this, you're likely to continue to run in to other compatibility problems (possibly right after you make that fix), but it might get this code working for now.
Keep in mind that basemap does not support Python 3, and is deprecated. The core developer of Basemap (Ben Root) is urging users to stop using it, because it's been abandoned and will not be ported to Python 3, even when Python 2 stops receiving security updates in 2020. He suggests using cartopy instead. All this was said by Ben Root at Scipy 2017.

How to make hexbin plots from a data file using seaborn?

I'm pretty new to using matplotlib and seaborn, and I couldn't really find any "for dummies" guides on how to do this. I keep getting error messages trying to use code from the guides I can find. I guess I'm having difficulty taking their pieces of code and knowing how to apply it to my problem.
I'd like to make a plot like the ones here: 1 and 2. I have a data file with two columns of data ranging from -180 to 180.
This is my attempt at the code:
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import kendalltau
sns.set(style="ticks")
f2 = open("dihs23")
lines = f2.readlines()
f2.close()
x = []
y = []
for line in lines:
p = line.split()
x.append(float(p[0]))
y.append(float(p[1]))
sns.jointplot(x, y, kind="hex", stat_func=kendalltau, color="#4CB391")
sns.plt.show()
Which returns the error
Traceback (most recent call last):
File "heatmap.py", line 30, in <module>
sns.jointplot(x, y, kind="hex", stat_func=kendalltau, color="#4CB391")
File "/usr/local/lib/python2.7/dist-packages/seaborn/distributions.py", line 973, in jointplot
xlim=xlim, ylim=ylim)
File "/usr/local/lib/python2.7/dist-packages/seaborn/axisgrid.py", line 1133, in __init__
x = x[not_na]
TypeError: only integer arrays with one element can be converted to an index
I'm guessing there's some aspect to the format of the data that is part of the problem, but I'm not sure how to fix it.
Thank you for the help!
Try transforming your lists to an array with NumPy
x_axis = np.asarray(x)
y_axis = np.asarray(y)

Python 3D isosurface from a set of 2D images

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

Categories