5

I'm using NumPy 1.6.2, SciPy 0.11.0, Matplotlib 1.1.1. Can I plot ribbons as in the picture?

enter image description here

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
  • What does your data look like? What have you tried? What isn't working about what you have tried? – tacaswell Apr 10 '13 at 16:24
  • The chart comes from a google search because I can't upload my chart made with Mathematica (due to my reputation too low). My chart represents a series of fluorescence spectra measured at different times. The spectral data can be tabulated so as to use a 3D plot area for each spectrum. I would like to obtain the same chart in Python. The matplotlib closer example it seems to be `trisurf3d_demo` but it requires matplotlib 1.2.0. I just would like to know if there is any alternative. –  Apr 10 '13 at 16:44
  • 1
    I suspect you can do this with `surf` + inserting `NaN` rows into your data, or a `surf` per ribbon. You might be better off looking at `mayavi` which is an opengl based renderer. – tacaswell Apr 10 '13 at 16:51
  • Yes. Each spectrum is a ribbon (surface) as in Mathematica. I can obtain the chart using `mlab.griddata` and then `plot_surface`. Thanks. –  Apr 10 '13 at 17:39
  • If you figured out how to do this can you write it up as an answer for future users? – tacaswell Apr 10 '13 at 19:49

3 Answers3

4

This is the full code.

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

data=np.genfromtxt('fluorescence_2.txt')
x=data[:,0]
fig=plt.figure()
ax=fig.gca(projection='3d')

for i in range(1,17,2):
    y=data[:,i]
    z=data[:,i+1]
    xi=np.linspace(min(x),max(x))
    yi=np.linspace(min(y),max(y))
    X,Y=np.meshgrid(xi,yi)
    Z=griddata(x,y,z,xi,yi)
    ax.plot_surface(X,Y,Z,rstride=50,cstride=1,cmap='RdYlBu')
    ax.set_zlim3d(np.min(Z),np.max(Z))

ax.set_title('Fluorescence spectra (WL ex = 350 nm)')
ax.set_xlabel('WL em (nm)')
ax.set_ylabel('Spectrum')
ax.set_yticks([])
ax.set_zlabel('Emission')
plt.show()
2

In my previous version was necessary to change the data table structure before the load into the script. The following version is my last and it plots the ribbons directly from the original data, a simple table of absorbances.

import itertools
import numpy as np
from matplotlib.mlab import griddata
from mpl_toolkits.mplot3d import Axes3D
from pylab import *
matplotlib.rcParams.update({'font.size':10})
spectra=loadtxt('C:/.../absorbance.txt')
fig=figure()
ax=fig.gca(projection='3d')
for i in range(0,7+1):
    y=spectra[:,i]
    x=sorted(range(1,len(y)+1)*2)
    a=[i,i+1]*len(y)
    b=list(itertools.chain(*zip(y,y)))
    xi=np.linspace(min(x),max(x))
    yi=np.linspace(min(a),max(a))
    X,Y=np.meshgrid(xi,yi)
    Z=griddata(x,a,b,xi,yi)
    ax.plot_surface(X,Y,Z,rstride=50,cstride=1,cmap='Spectral')
    ax.set_zlim3d(np.min(Z),np.max(Z))

ax.grid(False)
ax.w_xaxis.pane.set_visible(False)
ax.w_yaxis.pane.set_visible(False)
ax.w_zaxis.pane.set_color('gainsboro')
ax.set_title('Molecular spectra')
ax.set_xlim3d(0,23)
ax.set_xticks([1.6735,6.8367,12.0000,17.1633,22.3265])
ax.set_xticklabels(['350','400','450','500','550'])
ax.set_xlabel('Wavelength (nm)')
ax.set_yticks([0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5])
ax.set_yticklabels(['1','2','3','4','5','6','7','8'])
ax.set_ylabel('Spectrum')
ax.set_zlim3d(0,2)
ax.set_zlabel('Absorbance')
show()

Absorbance

  • In order to color each individual ribbon as in the original question you can use `facecolors` instead of colormap as described in [this answer to "how to use cmap on 3d x axis"](https://stackoverflow.com/a/42853616/1483986) – 7yl4r Oct 07 '17 at 04:02
1

Here is working code to create a ribbon plot. It is based off of the mplot3d example code: surface3d_demo.py and then modified to create ribbons. My code my not be the most efficient way to do it, but it works.

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np

#create data
x = np.linspace(-10,5,200)
y = np.linspace(-5,5,40)
xGrid, yGrid = np.meshgrid(y, x)
z = np.sin(np.sqrt(xGrid**2 + yGrid**2))

numPts = x.shape[0]
numSets = y.shape[0]

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

#plot each "ribbon" as a surface plot with a certain width
ribbonWidth = 0.75
for i in np.arange(0,numSets-1):
    X = np.vstack((x,x)).T
    Y = np.ones((numPts,2))*i
    Y[:,1] = Y[:,0]+ribbonWidth
    Z = np.vstack((z[:,i],z[:,i])).T
    surf = ax.plot_surface(X,Y,Z, rstride=1, cstride=1, cmap=cm.jet,
                           linewidth=0, vmin=-1, vmax=1)

ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
ax.set_xlabel('Data Points')
ax.set_ylabel('Data Set Number')
ax.set_ylim((0,numSets))
ax.set_zlabel('Z')
ax.set_zlim((-1, 1))
fig.colorbar(surf, shrink=0.5, aspect=5)

plt.show()
user2599816
  • 173
  • 1
  • 8