1

I am learning to visualize data with Matplotlib and I want to plot a line in a very customized fashion. I am new to Matplotlib and I do not know if what I want to do is even possible. Here goes:

Setting: Imagine that you have a set of random (x,y) points for a line graph. For illustrative purposes I compacted 4 different line graphs into one plot, but imagine if each of the 4 lines below were broken out into their own individual plots.

[![enter image description here][1]][1]

Step 1) For each of the line graphs A, B, C, D go to the global maximum and call it "X".

Step 4 possible?) Draw a line connecting "X" to "Y". Is it possible to draw this line in matplotlib? Thank you.

Relevant code below:

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(50, 4), 
        index=pd.date_range('1/1/2000', periods=50), columns=list('ABCD'))
df = df.cumsum()
df.plot();
MrPatterns
  • 4,184
  • 27
  • 65
  • 85
  • Can you include your data and code? – David Erickson Jan 12 '21 at 23:49
  • Done. Unfortunately there is no static data, it was randomly generated. – MrPatterns Jan 13 '21 at 00:21
  • see my answer to your question. There are obviously a million things you can formatting-wise, but if I have answered your question, then kindly accept as solution. Thank you! – David Erickson Jan 13 '21 at 00:37
  • Is your definition of local maximum the highest point that takes place in a point of time AFTER the maximum? If you are looking for something else, then please include code or methodologies you are going to use to find it. – David Erickson Jan 13 '21 at 01:50
  • ^^^ If that is why you are looking for, it’s pretty easy to get that in a pandas dataframe with the same structure as my current answer. I’ll update my answer when I get back from a workout. – David Erickson Jan 13 '21 at 01:52
  • "Is your definition of local maximum the highest point that takes place in a point of time AFTER the maximum?" ==> Yes. Step 3 specifies, in a very strict & unusual manner, exactly how to identify "Y", the local maximum. – MrPatterns Jan 13 '21 at 18:19

1 Answers1

1

I think your specific logic to calculate local maximums needs to be defined rather than just using the "clock" metaphor, but once you define that logic with scipi, pandas or another library, you can create a dataframe as I have. From there, you should be able to produce the result from below.

If you are okay with seaborn (built on top of matplotlib), I think it is a little bit easier, because you can pass the hue parameter to create all of the lines for each category in one line of code. You need to create a new dataframe with these lines that you want to plot. I do this by sorting the values and getting the tail value per group. See reproducible examples below.

Example 1 (plotting a local maximum):

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
fig, ax = plt.subplots()
plt.style.use('classic')
df = pd.DataFrame(np.random.randn(50, 4), 
        index=pd.date_range('1/1/2000', periods=50), columns=list('ABCD'))
df = df.cumsum()
df = df.melt(ignore_index=False).reset_index()
sns.lineplot(data=df, x="index", y="value", hue="variable", ax=ax)
lines_max = (df.sort_values('value').groupby("variable").tail(1)).sort_values('variable')
lines_local_max = df[((df['variable'] == lines_max['variable'].iloc[0]) & (df['index'] > lines_max['index'].iloc[0]))
                | ((df['variable'] == lines_max['variable'].iloc[1]) & (df['index'] > lines_max['index'].iloc[1]))
                | ((df['variable'] == lines_max['variable'].iloc[2]) & (df['index'] > lines_max['index'].iloc[2]))
                | ((df['variable'] == lines_max['variable'].iloc[3]) & (df['index'] > lines_max['index'].iloc[3]))]
lines_local_max = (lines_local_max.sort_values(['variable', 'value']).groupby("variable").tail(1))
lines = lines_max.append(lines_local_max).sort_values('variable')
lines
sns.lineplot(data=lines, x="index", y="value", hue="variable", marker="o",
             style='variable', dashes=[(2, 2), (2, 2), (2, 2), (2, 2)], legend=False, ax=ax)
x_dates = pd.to_datetime(df['index'].unique())
plt.xticks(x_dates[0::7], rotation=45, ha='center')
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b-%d-%Y'))

enter image description here

Example 2 (just drawing a line to the end and not defining local max - purpose is just to show you haow to draw a line from the max point to another defined point):

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
plt.style.use('classic')
fmri = sns.load_dataset("fmri")
fmri = fmri.groupby(['event', 'timepoint'], as_index=False)['signal'].mean()
sns.lineplot(data=fmri, x="timepoint", y="signal", hue="event")
lines_max = (fmri.sort_values('signal').groupby("event").tail(1))
lines_last = (fmri.sort_values('timepoint').groupby("event").tail(1))
lines = lines_max.append(lines_last)
sns.lineplot(data=lines, x="timepoint", y="signal", hue="event", marker="o", style='event', dashes=[(2, 2), (2, 2)])

enter image description here

Example 3 (another example drawing a line with the data you have provided to the end and not defining local max - purpose is just to show you how to draw a line from the max point to another defined point): example with the data you have provided:

import pandas as pd
import numpy as np
fig, ax = plt.subplots()
df = pd.DataFrame(np.random.randn(50, 4), 
        index=pd.date_range('1/1/2000', periods=50), columns=list('ABCD'))
df = df.cumsum()
df = df.melt(ignore_index=False).reset_index()
sns.lineplot(data=df, x="index", y="value", hue="variable", ax=ax)
lines_max = (df.sort_values('value').groupby("variable").tail(1))
lines_last = (df.sort_values('index').groupby("variable").tail(1))
lines = lines_max.append(lines_last).sort_values('variable')
sns.lineplot(data=lines, x="index", y="value", hue="variable", marker="o",
             style='variable', dashes=[(2, 2), (2, 2), (2, 2), (2, 2)], legend=False, ax=ax)
x_dates = df['index'].dt.strftime('%Y-%m-%d').sort_values().unique()
ax.set_xticklabels(labels=x_dates, rotation=45, ha='center')

enter image description here

David Erickson
  • 16,433
  • 2
  • 19
  • 35
  • @phan I thought you were more interested in figuring out the `matplotib` side of the problem. There are multiple ways to to find local maximum's and you might use different methodologies depending on the data and whatever else you are trying to achieve. From this answer (https://stackoverflow.com/questions/48023982/pandas-finding-local-max-and-min ) you can use two different methods from the top-voted answer. This question is incomplete without showing attempts to calculate local maximum's and what your desired output would be for the points that would qualify as a local maximum. – David Erickson Jan 13 '21 at 01:32
  • Hmm, I'm interested in knowing whether Matplotlib is capable of plotting from X to Y in the manner I described. I just don't know. If the answer's yes, I can code the method to arrive at the local maxima. My desired output is simply to connect points "X" and "Y" with a line. Let me think about this some more and see if I can produce code for determining point "Y" – MrPatterns Jan 13 '21 at 02:32
  • Also, I appreciate your help here. I'm not familiar with Seaborn at all but I'm willing to check it out and learn it. – MrPatterns Jan 13 '21 at 18:20
  • @phan no problem (see the first example), but I am guessing this will give you exactly what you are looking for. I would suggest showing the code you have tried to generate local maximum rather than the "clocks" example and in your title "but in an unusual fashion". You need to more clearly define the unusual fashion with example input and expected output data and show your attempt with the code. – David Erickson Jan 13 '21 at 19:54
  • @phan I think at this point it would be best to focus on that in a new question and reference back to this one. I would greatly appreciate if you could accept as answer and upvote if helpful. Thank you! – David Erickson Jan 13 '21 at 19:55
  • 1
    Agreed and Done! Thank you for the amount of time & effort & care you put into your responses. They have been very helpful in helping move faster along the path to understanding Matplotlib. Also, thanks for the introduction to Seaborn. – MrPatterns Jan 13 '21 at 22:49