Create 3D Plot Colored According to the Z-Axis

Create 3D Plot Colored According to the Z-axis

I modified your code a bit.

library(Sleuth2)

It's generally better practice to use the data argument than to use predictor variables extracted from a data frame via $:

mlr<-lm(Buchanan2000~Perot96*Gore2000,data=ex1222)

We can use expand.grid() and predict() to get the regression results in a clean way:

perot <- seq(1000,40000,by=1000)
gore <- seq(1000,400000,by=2000)

If you want the facets evaluated at the locations of the observations, you can use perot <- sort(unique(ex1222$Perot96)); gore <- sort(unique(ex1222$Gore2000)) instead.

pframe <- with(ex1222,expand.grid(Perot96=perot,Gore2000=gore))
mlrpred <- predict(mlr,newdata=pframe)

Now convert the predictions to a matrix:

nrz <- length(perot)
ncz <- length(gore)
z <- matrix(mlrpred,nrow=nrz)

I chose to go from light red (#ffcccc, red with quite a bit of blue/green) to dark red (#cc0000, a bit of red with nothing else).

jet.colors <- colorRampPalette( c("#ffcccc", "#cc0000") ) 

You could also use grep("red",colors(),value=TRUE) to see what reds R has built in.

# Generate the desired number of colors from this palette
nbcol <- 100
color <- jet.colors(nbcol)

# Compute the z-value at the facet centres
zfacet <- z[-1, -1] + z[-1, -ncz] + z[-nrz, -1] + z[-nrz, -ncz]
# Recode facet z-values into color indices
facetcol <- cut(zfacet, nbcol)

persp(perot, gore, z,
col=color[facetcol],theta=-30, lwd=.3,
xlab="Perot 96", ylab="Gore 2000", zlab="Predicted Votes for Buchanan")

Sample Image

You say you're "not super happy with the readability" of the plot, but that's not very specific ... I would spend a while with the ?persp page to see what some of your options are ...

Another choice is the rgl package:

library(rgl)
## see ?persp3d for discussion of colour handling
vertcol <- cut(z, nbcol)
persp3d(perot, gore, z,
col=color[vertcol],smooth=FALSE,lit=FALSE,
xlab="Perot 96", ylab="Gore 2000", zlab="Predicted Votes for Buchanan")

Sample Image

It might also be worth taking a look at scatter3d from the car package (there are other posts on SO describing how to tweak some of its graphical properties).

library(car)
scatter3d(Buchanan2000~Perot96*Gore2000,data=ex1222)

Sample Image

How to make 3D scatter plot color bar adjust to the Z axis size?

One way to modify the size of your color bar is to use "shrink" axes property like this:

plt.colorbar(three_d1, shrink=0.5) # an example

But you need to find the good value by hands.It could be 0.5 like 0.7.

However, you can try to calcul the needed value of shrink by getting the size of the z axis.

3D matplotlib: color depending on x axis position

Setting the color of a trisurf plot to something other than its Z values is not possible, since unfortunately plot_trisurf ignores the facecolors argument.

However using a normal surface_plot makes it possible to supply an array of colors to facecolors.

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

X,Y = np.meshgrid(np.arange(10), np.arange(10))
Z = np.sin(X) + np.sin(Y)
x = X.flatten()
y = Y.flatten()
z = Z.flatten()

fig = plt.figure(figsize=(9,3.2))
plt.subplots_adjust(0,0.07,1,1,0,0)
ax = fig.add_subplot(121, projection='3d')
ax2 = fig.add_subplot(122, projection='3d')
ax.set_title("trisurf with color acc. to z")
ax2.set_title("surface with color acc. to x")

ax.plot_trisurf(x,y,z , cmap="magma")

colors =plt.cm.magma( (X-X.min())/float((X-X.min()).max()) )
ax2.plot_surface(X,Y,Z ,facecolors=colors, linewidth=0, shade=False )

ax.set_xlabel("x")
ax2.set_xlabel("x")
plt.show()

Sample Image

Create 3D Plot (not surface, scatter), where colour depends on z values

The poster wants two things

  1. lines with colors depending on z-values
  2. animation of the lines over time

In order to achieve(1) one needs to cut up each line in separate segments and assign a color to each segment; in order to obtain a colorbar, we need to create a scalarmappable object that knows about the outer limits of the colors.

For achieving 2, one needs to either (a) save each frame of the animation and combine it after storing all the frames, or (b) leverage the animation module in matplotlib. I have used the latter in the example below and achieved the following:

Sample Image

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt, numpy as np
from mpl_toolkits.mplot3d.art3d import Line3DCollection

fig, ax = plt.subplots(subplot_kw = dict(projection = '3d'))

# generate data
x = np.linspace(-5, 5, 500)
y = np.linspace(-5, 5, 500)
z = np.exp(-(x - 2)**2)

# uggly
segs = np.array([[(x1,y2), (x2, y2), (z1, z2)] for x1, x2, y1, y2, z1, z2 in zip(x[:-1], x[1:], y[:-1], y[1:], z[:-1], z[1:])])
segs = np.moveaxis(segs, 1, 2)

# setup segments

# get bounds
bounds_min = segs.reshape(-1, 3).min(0)
bounds_max = segs.reshape(-1, 3).max(0)

# setup colorbar stuff
# get bounds of colors
norm = plt.cm.colors.Normalize(bounds_min[2], bounds_max[2])
cmap = plt.cm.plasma
# setup scalar mappable for colorbar
sm = plt.cm.ScalarMappable(norm, plt.cm.plasma)

# get average of segment
avg = segs.mean(1)[..., -1]
# get colors
colors = cmap(norm(avg))
# generate colors
lc = Line3DCollection(segs, norm = norm, cmap = cmap, colors = colors)
ax.add_collection(lc)

def update(idx):
segs[..., -1] = np.roll(segs[..., -1], idx)
lc.set_offsets(segs)
return lc

ax.set_xlim(bounds_min[0], bounds_max[0])
ax.set_ylim(bounds_min[1], bounds_max[1])
ax.set_zlim(bounds_min[2], bounds_max[2])
fig.colorbar(sm)

from matplotlib import animation
frames = np.linspace(0, 30, 10, 0).astype(int)
ani = animation.FuncAnimation(fig, update, frames = frames)
ani.save("./test_roll.gif", savefig_kwargs = dict(transparent = False))

fig.show()

how to change color of axis in 3d matplotlib figure?

You can combine your method with the approach provided here. I am showing an example that affects all three axes. In Jupyter Notebook, using tab completion after ax.w_xaxis.line., you can discover other possible options

ax.w_xaxis.line.set_visible(False)
ax.w_yaxis.line.set_color("red")
ax.w_zaxis.line.set_color("blue")

To change the tick colors, you can use

ax.xaxis._axinfo['tick']['color']='r'
ax.yaxis._axinfo['tick']['color']='g'
ax.zaxis._axinfo['tick']['color']='b'

To hide the ticks

for line in ax.xaxis.get_ticklines():
line.set_visible(False)

Sample Image

How to plot a 3D graph with Z axis being the magnitude of values in a csv?

A very minimal example, but I guess what you want to achieve is have each of your curves separated from the others in a 3D space. The code below generates two plots, one that draws curves individually, the other which treats the input as a surface. You can easily build onto this and achieve a more specific goal of yours I guess.

from mpl_toolkits.mplot3d import Axes3D  # noqa: F401 unused import
import matplotlib.pyplot as plt
import numpy

data = numpy.array([[28663, 4144, 6096, 6859, 7366, 7876, 8125],
[11268, 1374, 2119, 2393, 2615, 2809, 2904],
[14734, 2122, 3115, 3466, 3740, 4011, 4144],
[13341, 1452, 2322, 2689, 2877, 3114, 3238],
[18458, 2677, 3643, 4047, 4333, 4652, 4806],
[13732, 1621, 2224, 2502, 2704, 2930, 3020]])
fig, (ax, bx) = plt.subplots(nrows=1, ncols=2, num=0, figsize=(16, 8),
subplot_kw={'projection': '3d'})
for i in range(data.shape[1]):
ax.plot3D(numpy.repeat(i, data.shape[0]), numpy.arange(data.shape[0]),
data[:, i])
gridY, gridX = numpy.mgrid[1:data.shape[0]:data.shape[0] * 1j,
1:data.shape[1]:data.shape[1] * 1j]
pSurf = bx.plot_surface(gridX, gridY, data, cmap='viridis')
fig.colorbar(pSurf)
plt.show()

Sample Image



Related Topics



Leave a reply



Submit