Matplotlib – color bajo curva basado en color espectral

Me gustaría hacer un gráfico de un espectro, donde el área bajo la curva se sombreará de acuerdo con el color correspondiente de la luz. Al igual que esta ttwig:

introduzca la descripción de la imagen aquí

He intentado emular esto en matplotlib, usando imshow con el mapa de colores spectral para trazar los colores y un fill_between blanco para cubrir la región por encima de la curva. Estoy bastante contento con el resultado, excepto por dos cosas:

1) Los colores que estoy trazando no se alinean del todo con el espectro visible. Por ejemplo muestro 700 nm como amarillo / naranja, cuando está rojo. Estoy contento con una representación algo estilizada (por ejemplo, creo que los colores precisos que se muestran en la segunda respuesta aquí son aburridos), pero en general, me gustaría que las longitudes de onda se alineen con sus colores visibles.

2) Me gusta cómo el espectro anterior tiene las regiones fuera de la región visible coloreadas con alfa <1.0. No estoy seguro de cómo lograr esto.

Aquí está lo que tengo hasta ahora:

 import numpy as np import matplotlib.pyplot as plt fig, axs = plt.subplots(1, 1, figsize=(8,4), tight_layout=True) wavelengths = np.linspace(200, 1000, 1000) spectrum = (5 + np.sin(wavelengths*0.1)**2) * np.exp(-0.00002*(wavelengths-600)**2) plt.plot(wavelengths, spectrum, color='darkred') y = np.linspace(0, 6, 100) X,Y = np.meshgrid(wavelengths, y) X[X<400] = 400 extent=(np.min(wavelengths), np.max(wavelengths), np.min(y), np.max(y)) plt.imshow(X, clim=(350,820), extent=extent, cmap=plt.get_cmap('spectral'), aspect='auto') plt.xlabel('Wavelength (nm)') plt.ylabel('Intensity') plt.fill_between(wavelengths, spectrum, 8, color='w') plt.savefig('WavelengthColors.png', dpi=200) plt.show() 

En primer lugar, necesitaría una función que tome la longitud de onda como entrada y devuelva un color RGB. Tal función se puede encontrar aquí . Uno puede adaptarlo para devolver también un valor alfa, que es más pequeño 1 fuera del rango de colores visibles.

Esta función se puede utilizar para crear un mapa de colores. El uso de una normalización decente permite que el rango de longitudes de onda se asigne al rango entre 0 y 1, de manera que este mapa de colores se pueda usar en una gráfica de imagen.

introduzca la descripción de la imagen aquí

 import numpy as np import matplotlib.pyplot as plt import matplotlib.colors def wavelength_to_rgb(wavelength, gamma=0.8): ''' taken from http://www.noah.org/wiki/Wavelength_to_RGB_in_Python This converts a given wavelength of light to an approximate RGB color value. The wavelength must be given in nanometers in the range from 380 nm through 750 nm (789 THz through 400 THz). Based on code by Dan Bruton http://www.physics.sfasu.edu/astro/color/spectra.html Additionally alpha value set to 0.5 outside range ''' wavelength = float(wavelength) if wavelength >= 380 and wavelength <= 750: A = 1. else: A=0.5 if wavelength < 380: wavelength = 380. if wavelength >750: wavelength = 750. if wavelength >= 380 and wavelength <= 440: attenuation = 0.3 + 0.7 * (wavelength - 380) / (440 - 380) R = ((-(wavelength - 440) / (440 - 380)) * attenuation) ** gamma G = 0.0 B = (1.0 * attenuation) ** gamma elif wavelength >= 440 and wavelength <= 490: R = 0.0 G = ((wavelength - 440) / (490 - 440)) ** gamma B = 1.0 elif wavelength >= 490 and wavelength <= 510: R = 0.0 G = 1.0 B = (-(wavelength - 510) / (510 - 490)) ** gamma elif wavelength >= 510 and wavelength <= 580: R = ((wavelength - 510) / (580 - 510)) ** gamma G = 1.0 B = 0.0 elif wavelength >= 580 and wavelength <= 645: R = 1.0 G = (-(wavelength - 645) / (645 - 580)) ** gamma B = 0.0 elif wavelength >= 645 and wavelength <= 750: attenuation = 0.3 + 0.7 * (750 - wavelength) / (750 - 645) R = (1.0 * attenuation) ** gamma G = 0.0 B = 0.0 else: R = 0.0 G = 0.0 B = 0.0 return (R,G,B,A) clim=(350,780) norm = plt.Normalize(*clim) wl = np.arange(clim[0],clim[1]+1,2) colorlist = list(zip(norm(wl),[wavelength_to_rgb(w) for w in wl])) spectralmap = matplotlib.colors.LinearSegmentedColormap.from_list("spectrum", colorlist) fig, axs = plt.subplots(1, 1, figsize=(8,4), tight_layout=True) wavelengths = np.linspace(200, 1000, 1000) spectrum = (5 + np.sin(wavelengths*0.1)**2) * np.exp(-0.00002*(wavelengths-600)**2) plt.plot(wavelengths, spectrum, color='darkred') y = np.linspace(0, 6, 100) X,Y = np.meshgrid(wavelengths, y) extent=(np.min(wavelengths), np.max(wavelengths), np.min(y), np.max(y)) plt.imshow(X, clim=clim, extent=extent, cmap=spectralmap, aspect='auto') plt.xlabel('Wavelength (nm)') plt.ylabel('Intensity') plt.fill_between(wavelengths, spectrum, 8, color='w') plt.savefig('WavelengthColors.png', dpi=200) plt.show()