Multiple lineplots with a dependency variable

Viewed 89

One dataframe contains the percentage change of several nerve amplitudes at the corresponding measurement points and a second their respective diameters.

>>> amp.head()
   Timepoint 1  Timepoint 2  Timepoint 3  Timepoint 4  Timepoint 5  Timepoint 6
0     1.277778     0.944444     0.444444          0.0          0.0          0.0
1     0.941176     0.705882     0.352941          0.0          0.0          0.0
2     0.818182     0.490909     0.309091          0.0          0.0          0.0
3     1.000000     0.658537     0.414634          0.0          0.0          0.0
4     0.588235     0.455882     0.323529          0.0          0.0          0.0

>>> dia.head()
0    1.3
1    1.1
2    1.2
3    1.5
4    1.6

I would like to plot the amplitudes in relation to their diameter, as hue='insert column here' does in many Seaborn plots.

def plot_rows(df, color="xkcd:red"):
    # Reference: https://stackoverflow.com/questions/32105817/plot-entire-row-on-pandas

    number = df.shape[0]
    rows = range(number)
    fig, ax = plt.subplots(figsize=(8, 8))
    plt.show()

Although this function does plot the amplitudes row by row, it doesn't show their diameter. Is it possible to do so any other way?

I thought about combining both datasets, sort them according to the diameter, divide the diameter in let's say 3 groups and then plot them all on the same graph with different colors, but I just can't figure out how.

Something like this was my first take

new_df = pd.concat([amp, dia], axis=1)
# sort the values into categories and exclude "Diameter [mm]" from the plot
small = new_df[(new_df["Diameter [mm]"] < max(new_df["Diameter [mm]"]) / 2)][:-1]
plot_rows(small[[small.columns.values]])
1 Answers

Great question. I had to do some learning to come up with this answer. Perhaps somebody else can add to this post, maybe there is an easier or better solution!

I put your subset data into a CSV and placed it to a dataframe (amp) and series (dia). Your data are much easier to plot if you simply take the transpose of amp.

>>> amp.T
>>>
                   0           1           2           3           4
Timepoint 1 1.277778    0.941176    0.818182    1.000000    0.588235
Timepoint 2 0.944444    0.705882    0.490909    0.658537    0.455882
Timepoint 3 0.444444    0.352941    0.309091    0.414634    0.323529
Timepoint 4 0.000000    0.000000    0.000000    0.000000    0.000000
Timepoint 5 0.000000    0.000000    0.000000    0.000000    0.000000
Timepoint 6 0.000000    0.000000    0.000000    0.000000    0.000000

I was able to get an answer together and apply it to your dataset using the following posts/resources.
Line collection tutorial
Excellent post by @ahwillia

Basically you use the LineCollection class to apply a colormap to the lines with mpl.cm.ScalarMappable.set_array and then plot them at the same time by adding the collection to the axis. This also facilitates use of a colorbar. Reviewing these documentation may help you understand the implementation better and apply it more broadly to datasets. Here is an implementation for your data.

enter image description here

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.collections import LineCollection
from matplotlib import ticker

#read CSV and define diameter
amp = pd.read_csv('stack_overflow.csv')
dia = pd.Series([1.3, 1.1, 1.2, 1.5, 1.6])

fig, ax = plt.subplots(figsize=(12,8))

#xs are a len(timepoints) X len(timepoints) matrix one row of xs for each line
#ys are a len(timepoints) X len(timepoints) matrix one row of ys for each line
xs = [np.arange(len(amp.columns))] * len(amp.columns)
ys = amp.values

#segments is an array of x/y pairs, such as multiple of the following
# [[0.  1.277778]
 # [1.  0.944444]
 # [2.  0.444444]
 # [3.  0.      ]
 # [4.  0.      ]
 # [5.  0.      ]]
segments = [np.column_stack([x, y]) for x,y in zip(xs, ys)]
#create LineCollection object and pass **kwargs
lines = LineCollection(segments, cmap = 'viridis', linewidth=3.5)

#set_array is a cmap function that takes the dia values and normalizes them
#then maps to the colormap provided to lines
lines.set_array(dia)

#plot lines and scale axis
ax.add_collection(lines)
ax.autoscale()

#add colorbar
ax_cb = fig.colorbar(lines)

#styling
hide_spines = [ax.spines[s].set_visible(False) for s in ['top','right']]
tic_params  = ax.tick_params(axis='both', labelsize=14, bottom=True, left=False)
x_ticks     = ax.set_xticks(np.arange(len(amp.columns)))
tick_labels = ax.set_xticklabels(amp.columns)
y_label     =  ax.set_ylabel('Amplitude')

Alternatively, if you just want to plot the lines and color them, it's quite simple to do with pandas plotting on amp.T with a seaborn colormap. Though I couldn't find a way to show the colorbar with this implementation

enter image description here

amp.T.plot(color=sns.color_palette("Set2", 8), figsize=(12,8), linewidth=3.5)
ax = plt.gca()
ax.legend(edgecolor='w', fontsize=14)
hide_spines = [ax.spines[s].set_visible(False) for s in ['top','right']]
tic_params  = ax.tick_params(axis='both', labelsize=14, bottom=True, left=False)
x_ticks     = ax.set_xticks(np.arange(len(amp.columns)))
tick_labels = ax.set_xticklabels(amp.columns)
y_label     =  ax.set_ylabel('Amplitude', fontsize=14)
Related