-1

Suppose you have the following data:

x1_Length = 100
x1 = np.linspace(10,100,num=x1_Length)
y1 = np.linspace(0, 1.3, num=x1_Length)

x2_Length = 200
x2 = np.linspace(50, 200, num=x2_Length)
y2 = np.exp(np.linspace(0, 1, num=x2_Length))

If you wanted to plot this, it would be straightforward:

fig, ax = plt.subplots()
ax.plot(x1,y1, ".g")
ax.plot(x2,y2, "--r")

Which gives an expected result, EVEN THOUGH THE ARRAYS THAT WERE PLOTTED ARE OF DIFFERENT LENGTHS AND DO NOT SHARE THE x-data.

enter image description here

NOW, suppose you wanted to plot this same data in an intensity plot. This causes some problems:

  1. it is my understanding that an intensity plot (for example imshow) plots 2D arrays and as such have no direct concept of an x-axis scale or y-axis scale. Data is presented based on its index in the array. Here, this would be a problem because the first elements of the two arrays DO NOT correspond to the same x-value.

  2. all the rows/columns need to be of the same length in a 2D array. This is the minor of the two problems, as i could always figure out the longest dataset and pad all shorter ones with, for example, NaNs, like so:

dataset = [x1_data, x2_data]
max_Length = np.max([array[0,:].shape[0] for array in dataset ])
data_array = np.nan * np.ones((2*len(dataset), max_Length))
for i, spectrum_array2D in enumerate(dataset):
    length = spectrum_array2D[0,:].shape[0]
    firstRowOfInsertion = 2*i
    data_array[[firstRowOfInsertion, firstRowOfInsertion+1], :length] = spectrum_array2D

I don't see any technological limitation why one should not be able to plot several arrays of different data into an intensity plot much like plt.plot()can. Ideally, what I need is to tell the intensity plot that it can expect, in this example, 2 horizontal slices of data, and then supply the x-data (plotted againt the x-axis), the y-data (plotted against the colorbar) for each index of the slice into which this plot should go. Do you know of a way to achieve this, or any package capable of this?

EDIT: Ultimately, I want a plot that can look like this, which was adopted from @wtw suggestion below (the data in this plot is random, I need it to be my y1 and y2 arrays):

enter image description here

  • Your datasets are 1D in the first part, 2D in the second. The x- and y- coordinates tell matplotlib where to draw a dot on the 2D monitor (actually it's line-segments). What do you expect your output to look like in the second case? – Mad Physicist Jan 06 '22 at 16:09
  • So basically, capable of what, exactly? – Mad Physicist Jan 06 '22 at 16:10
  • 1) pcolormesh also plots 2-d arrays and they have an xscale and yscale. You can also fake an x/y scale in imshow using the `extents` kwarg. 2) neither pcolormesh nor imshow require the number of columns to equal the number of rows. – Jody Klymak Jan 06 '22 at 16:19

4 Answers4

1

One option with matplotlib is pcolormesh: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.pcolormesh.html

This accepts coordinates along with values. In this case, you have a 2D grid, so you require a 2D array for the x coordinates, a 2D array for the y coordinates and a 2D array for the values.

You can plot different datasets, though if their coordinates overlap you'll have to think how you want these displayed. One option is to make the second dataset slightly transparent so you can see the first one through it.

import numpy as np
import matplotlib.pyplot as plt

# Create the first set of coordinates
X1, Y1 = np.meshgrid(np.arange(0, 10, 1), np.arange(0, 5, 0.5))
C1 = np.random.uniform(size=np.shape(X1))

# Second set of coordinates with smaller cells, slightly overlapping the first
X2, Y2 = np.meshgrid(np.arange(8, 12, 0.1), np.arange(4, 6, 0.1))
C2 = np.random.uniform(size=np.shape(X2))

# Plotting:
fig, ax = plt.subplots(1)

ax.pcolormesh(X1, Y1, C1)
ax.pcolormesh(X2, Y2, C2, alpha=0.25)

pcolormesh example

wtw
  • 678
  • 4
  • 9
  • Basically, this is ALMOST what I need, except that the arrays I want to plot are not 2D, but 1D. Essentially what i need is `X1, Y1 = np.meshgrid(np.arange(0, 10, 1), [0])`. This will not plot for some reason, whereas `X1, Y1 = np.meshgrid(np.arange(0, 10, 1), [0,1])` will. Both of them are 2D arrays, although the first one has shape (10,1) and the second has shape (10,2). But yes, if I could plot a 2D array consisting of only 1 row this is what I need.... – Douglas James Bock Jan 06 '22 at 17:58
  • I think that ```pcolormesh``` requires all 4 corners of a cell to plot it - there's a helpful diagram in the ```X,Y``` parameters in the documentation. – wtw Jan 06 '22 at 18:12
  • I may have misunderstood your question - if each line has arrays ```x, y, c```, and you wish the point on the line ```x[i], y[i]``` to have color ```c[i]``` then something like this should help: https://stackoverflow.com/questions/36505587/color-line-by-third-variable-python – wtw Jan 06 '22 at 18:22
  • no, i think you understood correctly the first time. The difference between your code and my real life code is that you created 2D arrays and just plotted those. I have got just 1D arrays.... Imagine if your "bright" data was only one horizontal slice, say at a y-axis (vertical scale) value of 0. And the "dark" data was at a y-axis scale of 5. Instead of two 2D arrays in your plot, you'd get 2 1D slices. That is what I need. – Douglas James Bock Jan 06 '22 at 18:27
  • ... so that i would get a plot like this: https://imgur.com/a/GHQHv8p – Douglas James Bock Jan 06 '22 at 18:32
  • i have figured out a way based on your answer, which I posted below. I have accepted your answer for getting me on the right track. thanks – Douglas James Bock Jan 06 '22 at 19:11
  • How does this look to you? https://imgur.com/byY19le – wtw Jan 06 '22 at 19:18
  • yes nice. could you post the code to that also as an answer. then I will accept that instead – Douglas James Bock Jan 06 '22 at 19:23
1

Following discussions, I think what is wanted is a line plotted with x points and colour values, possibly with a different y value each time.

A slight modification on the answer here (just to pass plotting variables through): Color line by third variable - Python

import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np

def plot_colourline(x,y,c, **xargs):
    c = cm.viridis((c-np.min(c))/(np.max(c)-np.min(c)))
    ax = plt.gca()
    for i in np.arange(len(x)-1):
        ax.plot([x[i],x[i+1]], [y[i],y[i+1]], c=c[i], **xargs)
    return

# First data set
x1 = np.arange(0, 10, 1)
y1 = np.zeros(len(x1))  # use 0 as the y value for all points
c1 = np.random.uniform(size=len(x1))

# Second dataset, different number of points, x values and y value
x2 = np.arange(8, 12, 0.1)
y2 = np.ones(len(x2))  # Use 1 as the y value for all points
c2 = np.random.uniform(size=len(x2))

fig, ax = plt.subplots(1)
plot_colourline(x1, y1, c1, linewidth=5)
plot_colourline(x2, y2, c2, linewidth=5, alpha=0.5)

line plot with color

wtw
  • 678
  • 4
  • 9
0

As far as I understand, you are aware, that the two datasets can not intersect each other. That's the first important condition. If to values would occupy the same pixel in the meshgrid, which one would be chosen? But even if the data does not intersect, there could be a problem when both axis have different sampling rates/distances. Before you could plot both of them in the same image or even mesh, you would have to resample/interpolate both arrays on an equal grid for both. This get's really tricky quite quickly and is prone to errors. If I were to try this, I would start by creating to seperate color maps, with the same colorcode, but two indipendent meshs. The next step would be to just lay one of figures on top of the other and shift it so that the axis line up; basically an inset. I suppose there is some best practice for that in matplotlib.

0

Working of wtw's first answer I have figured out a way, which is a bit hacky... With their method I could not plot my 1D arrays, so I made them 2D by stacking 2 identical arrays on top of each other. I then specify at what y-axis value these should be plotted. This is my final code:

import numpy as np
import matplotlib.pyplot as plt

x1_Length = 100
x1 = np.linspace(10,100,num=x1_Length)
z1 = np.linspace(0, 1.3, num=x1_Length)
x1_data = np.stack([x1,z1])

x2_Length = 200
x2 = np.linspace(50, 200, num=x2_Length)
z2 = np.exp(np.linspace(0, 4, num=x2_Length))
x2_data = np.stack([x2,z2])

x3_Length = 30
x3 = np.linspace(50, 150, num=x3_Length)
z3 = np.sin(np.linspace(0, 4, num=x3_Length))
x3_data = np.stack([x3,z3])

y_data = [-1, 5, 12]
stepsize = np.min(np.abs(np.diff(y_data)))
dataset = [x1_data, x2_data, x3_data]

fig, ax = plt.subplots(1)

for i, spectrum_array2D in enumerate(dataset):
    X, Y = np.meshgrid(spectrum_array2D[0,:], [y_data[i]-stepsize/4, y_data[i]+stepsize/4]); print(f"X has shape {X.shape}, Y has shape {Y.shape}")
    Z = np.vstack( [spectrum_array2D[1,:], spectrum_array2D[1,:]] ); print(f"Z has shape {Z.shape}")
    ax.pcolormesh(X, Y, Z)

ax.set_yticks(y_data)
ax.set_xlabel("x")
ax.set_ylabel("y")

which produces the following figure:

enter image description here