0

I have a script which analyses a dataset and then outputs xyz data. In order to understand the distribution of the data, I want to visualize it in a 3d plot. As I have no experience what so ever with using matplotlib, I just copied the code from here and expected it to work with my text file which looks like this:

-0.9 -0.9 483
-0.9 -0.7 224
-0.9 -0.5 156
-0.9 -0.3 153
-0.9 -0.1 174
-0.9 0.1 268
-0.9 0.3 95
-0.9 0.5 59
-0.9 0.7 50
-0.9 0.9 199
-0.7 -0.9 917
-0.7 -0.7 244
-0.7 -0.5 208
-0.7 -0.3 148
-0.7 -0.1 139
-0.7 0.1 98
-0.7 0.3 52
-0.7 0.5 56
-0.7 0.7 60
-0.7 0.9 221
...

However, once I start the script, I get the following error which leads to the colorbar being displayed incorrectly:

Warning (from warnings module):
   File "C:\Program Files\Python35\lib\site-packages\matplotlib\colors.py", line 496
     cbook._putmask(xa, xa < 0.0, -1)
RuntimeWarning: invalid value encountered in less

Furthermore, the plot has these triangles on its edges. I'm not sure whether they are a consequence of the above mentioned error as well. This is the output: enter image description here

Here's my code:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
from matplotlib.mlab import griddata
import numpy as np

fig = plt.figure()
ax = fig.gca(projection='3d')

data = np.genfromtxt('plot.txt')
x = data[:,0]
y = data[:,1]
z = data[:,2]

xi = np.linspace(-1, 1)
yi = np.linspace(-1, 1)

X, Y = np.meshgrid(xi, yi)
Z = griddata(x, y, z, xi, yi, interp='linear')

surf = ax.plot_surface(X, Y, Z, rstride=5, cstride=5, cmap=cm.jet,
                   linewidth=1, antialiased=True)

ax.set_zlim3d(np.min(Z), np.max(Z))

fig.colorbar(surf)

plt.show()

EDIT 1: I edited the source code to print xa before the offending line, which outputs:

[  nan   nan   nan   nan   nan   nan   nan   nan   nan   nan   nan  256.
256.  256.  256.  256.  256.  256.  256.   nan   nan  256.  256.  256.
256.  256.  256.  256.  256.   nan   nan  256.  256.  256.  256.  256.
256.  256.  256.   nan   nan  256.  256.  256.  256.  256.  256.  256.
256.   nan   nan  256.  256.  256.  256.  256.  256.  256.  256.   nan
nan  256.  256.  256.  256.  256.  256.  256.  256.   nan   nan  256.
256.  256.  256.  256.  256.  256.  256.   nan   nan  256.  256.  256.
256.  256.  256.  256.  256.   nan   nan   nan   nan   nan   nan   nan
nan   nan   nan   nan]

So I clearly have some NaN values here, but I'm not sure where they come from.

TheJD
  • 3
  • 4
  • This is a warning, not an error, hence you do get a plot out. It's really hard to know what's going on without a [mcve] of the issue, i.e. we do not have your data. Best try to generate some data within the code to reproduce the issue. – ImportanceOfBeingErnest Oct 20 '17 at 08:11
  • If you go to the source code and put `print(xa)` before the offending line, you'll see the array contains nan values and that's because your `Z` contains nan values. – Reti43 Oct 20 '17 at 08:17
  • @ImportanceOfBeingErnest Thank you for your fast response and for editing my post. I did provide you with my data. The [link above](https://pastebin.com/raw/UsQ5eArF) contains all the xyz coordinates I have. – TheJD Oct 20 '17 at 08:24
  • @Reti43 Thank you for your hint. I edited my question accordingly! – TheJD Oct 20 '17 at 08:32

1 Answers1

1

The problem is that griddata cannot produce data for the edges of the grid. This is circumvented internally by masking the output array. However, for a masked array, a comparison xa < 0, which is needed to determine the colors, is not possible.

The solution here would be to exclude the edges from plotting.

ax.plot_surface(X[1:-1,1:-1], Y[1:-1,1:-1], Z[1:-1,1:-1])

Complete example:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
from matplotlib.mlab import griddata
import numpy as np

fig = plt.figure()
ax = fig.gca(projection='3d')

data = np.genfromtxt('plot.txt')
x = data[:,0]
y = data[:,1]
z = data[:,2]

xi = np.linspace(-1, 1)
yi = np.linspace(-1, 1)

X, Y = np.meshgrid(xi, yi)
Z = griddata(x, y, z, xi, yi, interp='linear')

surf = ax.plot_surface(X[1:-1,1:-1], Y[1:-1,1:-1], Z[1:-1,1:-1], 
                       rstride=5, cstride=5, cmap=cm.jet,
                       linewidth=1, antialiased=True)

ax.set_zlim3d(np.min(Z), np.max(Z))

fig.colorbar(surf)

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 1
    While `griddata` produces masked arrays for data outside your range, `plot_surface` doesn't support that yet. The function is located in the file `site-packages\mpl_toolkits\mplot3d\axes3d.py` and they have a todo note for supporting this. At the moment they just do `X, Y, Z = np.broadcast(X, Y, Z)`, which destroys any masking and your nan values are exposed. – Reti43 Oct 20 '17 at 09:09