0

I have spectral data and would like to compare multiple plots together. For visualization purpose, I want to fill the area under the plot with the smallest plot always in the front. Here is a small area of the plot unfilled. enter image description here

In the first peak, from least to greatest is A, B, C, D, E. The second peak is reversed: E, D, C, B, A. Elsewhere in the plot, the order might change and could be non-sequential (A,D,C,E,B).

Here is what I want the final result to look like: final product

I've been messing with fill_between, but I'm having trouble when the peaks shift in intensity.

Here is a section of the df for testing purposes:

             A      B      C       D       E
spectra                                     
1.60       628    807    829     973     966
1.62      1173   1420   1620    1793    1968
1.64      3567   4512   4887    5783    6232
1.66     10365  13056  14254   17340   18286
1.68     23984  30503  33717   40242   43373
1.70     42923  54741  60679   72537   78680
1.72     59167  75363  83009   99574  107083
1.74     62070  78808  87334  104371  112352
1.76     49772  63391  69182   84503   90189
1.78     31434  39861  43771   52512   56310
1.80     16219  20637  22614   26787   28825
1.82      7632   9828  10545   12298   13602
1.84      4094   4920   5197    6192    6647
1.86      2398   2738   2851    3411    3593
1.88      1433   1508   1598    1781    1804
1.90       918    895    870     858     903
1.92       693    600    595     564     546
1.94       702    622    529     468     430
1.96       637    600    507     430     396
1.98       580    535    511     461     437
2.00       530    534    482     416     449
2.02       568    453    464     387     404
2.04       421    365    353     317     318
2.06       357    327    290     263     235
2.08       344    292    245     226     188
2.10       320    269    200     182     153
2.12       321    263    235     185     162
2.14       407    304    279     197     145
2.16       681    527    429     274     191
2.18      1537   1140    920     580     382
2.20      3962   2789   2288    1321     768
2.22      9686   6951   5331    3050    1765
2.24     21056  15007  11416    6436    3752
2.26     38267  26874  20562   11561    6637
2.28     55706  39067  29799   16900    9596
2.30     64310  45348  34907   19514   11307
2.32     59707  42021  31802   18299   10180
2.34     44050  31169  23458   13561    7769
2.36     25907  18337  13965    7889    4576
2.38     13059   9341   6997    3978    2401
2.40      6294   4539   3472    1966    1296
2.42      4272   3028   2389    1391     892
2.44      3995   2956   2312    1344     862
2.46      3970   2898   2267    1351     862
2.48      3496   2522   2055    1153     731
2.50      2512   1815   1512     938     613
  • 1
    AFAIK, this is not directly possible. You'll have to split your peaks first, then order them for the fill per group. Also this becomes even more complex is the peaks are not perfectly aligned and ordered. For instance, what should happen if a peak has one part above / one part below another peak? – mozway Dec 13 '22 at 05:49
  • You didn't include your plot code, but depending on how you did, you have some control. [Check out this Q & A.](https://stackoverflow.com/questions/37246941/specifying-the-order-of-matplotlib-layers) – Kat Dec 13 '22 at 06:17

1 Answers1

0

The following approach uses fill_between for each pair of successive x-values, and sorts the y values. Apparently, successive fill_betweens sometimes leave some little gaps. To work around that problem, the code plots all chunks that have the same order in one go.

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from io import StringIO

data_str = '''spectra             A      B      C       D       E                            
1.60       628    807    829     973     966
1.62      1173   1420   1620    1793    1968
1.64      3567   4512   4887    5783    6232
1.66     10365  13056  14254   17340   18286
1.68     23984  30503  33717   40242   43373
1.70     42923  54741  60679   72537   78680
1.72     59167  75363  83009   99574  107083
1.74     62070  78808  87334  104371  112352
1.76     49772  63391  69182   84503   90189
1.78     31434  39861  43771   52512   56310
1.80     16219  20637  22614   26787   28825
1.82      7632   9828  10545   12298   13602
1.84      4094   4920   5197    6192    6647
1.86      2398   2738   2851    3411    3593
1.88      1433   1508   1598    1781    1804
1.90       918    895    870     858     903
1.92       693    600    595     564     546
1.94       702    622    529     468     430
1.96       637    600    507     430     396
1.98       580    535    511     461     437
2.00       530    534    482     416     449
2.02       568    453    464     387     404
2.04       421    365    353     317     318
2.06       357    327    290     263     235
2.08       344    292    245     226     188
2.10       320    269    200     182     153
2.12       321    263    235     185     162
2.14       407    304    279     197     145
2.16       681    527    429     274     191
2.18      1537   1140    920     580     382
2.20      3962   2789   2288    1321     768
2.22      9686   6951   5331    3050    1765
2.24     21056  15007  11416    6436    3752
2.26     38267  26874  20562   11561    6637
2.28     55706  39067  29799   16900    9596
2.30     64310  45348  34907   19514   11307
2.32     59707  42021  31802   18299   10180
2.34     44050  31169  23458   13561    7769
2.36     25907  18337  13965    7889    4576
2.38     13059   9341   6997    3978    2401
2.40      6294   4539   3472    1966    1296
2.42      4272   3028   2389    1391     892
2.44      3995   2956   2312    1344     862
2.46      3970   2898   2267    1351     862
2.48      3496   2522   2055    1153     731
2.50      2512   1815   1512     938     613'''
df = pd.read_csv(StringIO(data_str), delim_whitespace=True).set_index('spectra')
xs = df.index.values
rows = df.values
colors = plt.cm.Set1.colors
fig, ax = plt.subplots(figsize=(15, 5))

prev_ord = None
prev_xs = []
prev_rows = []
for x, row in zip(xs, rows):
     ord = np.argsort(row)[::-1]
     if (ord != prev_ord).any():
          if prev_ord is not None:
               for i in prev_ord:
                    ax.fill_between(prev_xs, [r[i] for r in prev_rows], color=colors[i])
          prev_ord = ord
          prev_xs = [x]
          prev_rows = [row]
     else:
          prev_xs.append(x)
          prev_rows.append(row)
if prev_ord is not None:  # plot last chunk
     for i in prev_ord:
          ax.fill_between(prev_xs, [r[i] for r in prev_rows], color=colors[i])

handles = [plt.Rectangle((0, 0), 0, 0, color=color, label=lbl) for color, lbl in zip(colors, df.columns)]
ax.legend(handles=handles)
ax.margins(x=0)
ax.set_ylim(ymin=0)
plt.tight_layout()
plt.show()

fill_between with overlapping curves in different orders

JohanC
  • 71,591
  • 8
  • 33
  • 66