3

I have x, y data for paths in the following format (sample only for illustration):

   seq    p1      p2
0  20      2      3
1  20      2      4
2  20      4      4
3  22      5      5
4  22      5      6
5  23      6      2
6  23      6      3
7  23      6      4

Each path has number of points and they are identified by a seq, points belonging to same seq is considered to be one path and so on..

I have plotted these paths(using my real data which is in same format as above)using the following code and also have attached the result:

import matplotlib.pyplot as plt

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

for (key, grp) in df.groupby("seq"):
    grp.plot(linestyle = "solid", x="p1", y="p2", ax = ax, label = key)

box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.title("Paths")
plt.show()

I have plotted some 40 paths, now the problem is how should I identify that which path is for uid 184, or which one is uid-194 ? They both are labelled with same color in the legend. Is there a way that I am able to identify each path distinctively, maybe labelling somewhere on the path(but that might make the graph cluttered).

My second question is I want to mark the starting and ending points of each path/ trajectory. Like start-point can be green and the end-point can be red. For example in the above sample df, for uid-20 the starting points are (2,3) in row 0 and end-points are (4,4) in row 2. Please suggest a way to mark these starting and ending points for each path in the df.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Liza
  • 961
  • 3
  • 19
  • 35

1 Answers1

2

I am not sure how much this answers your question, but this is a way to make the line colors compliant with a colormap, which usually helps me visualize different lines and their trends easier, but might not be very convenient to single out a single line.

cmap = plt.cm.get_cmap('viridis')
groups = df.groupby("uid")
ngroups = len(groups)

for i, (key, grp) in enumerate(groups):
    grp.plot(linestyle="solid", x="px", y="py", ax=ax, label=key, color=cmap(i/(ngroups-1))

to add markers for the first and last points, just single them out and assign them the color and marker you like. So, rewriting the for loop above:

for i, (key, grp) in enumerate(df.groupby("uid")):
    grp.plot(linestyle="solid", x="px", y="py", ax=ax, label=key, color=cmap(i/(ngroups-1)))
    grp.iloc[[0]].plot(marker="o", x="px", y="py", ax=ax, color='r', legend=False)
    grp.iloc[[-1]].plot(marker="o", x="px", y="py", ax=ax, color='g', legend=False)

if you want each marker to have a different tone of red and green, you can use the Reds and Greens colormaps such as:

start_cmap =  plt.cm.get_cmap('Reds')
end_cmap =  plt.cm.get_cmap('Greens')

and in the loop it will be

grp.iloc[[0]].plot(marker="o", x="px", y="py", ax=ax, color=start_cmap(i/(ngroups-1)), legend=False)
grp.iloc[[-1]].plot(marker="o", x="px", y="py", ax=ax, color=end_cmap(i/(ngroups-1)), legend=False)

EDITS

Handling Legends

To plot only the lines and not the end points markers, we use the fact that we have first plotted the line and then the two markers, and this is how the plots are pushed into the axis line queue, so we skip over the markers and explicitly tell the legend which lines to consider:

ax.legend(ax.lines[::3], groups.groups.keys(), loc='center left', bbox_to_anchor=(1, 0.5))

Using Colorbar

If using a colormap for the lines, it is useful to display a colorbar rather than legend, so we use something like this:

from matplotlib.colorbar import ColorbarBase
import matplotlib as mp

values = list(groups.groups.keys())
cax = fig.add_axes([0.92, 0.12, 0.02, 0.75])
cbar = ColorbarBase(cax, cmap=cmap, format='%d', ticks=values, drawedges=False, norm=mp.colors.Normalize(vmin=min(values), vmax=max(values)))
Gerges
  • 6,269
  • 2
  • 22
  • 44
  • Thank you so much, yep the first part is tricky, I am not sure if it's even possible to completely single out the lines, that too when there are large number of trajectories. For the second part, the marker at the start and end of the lines are of the same color at the lines itself, I want to specifically keep the start marker as red and end marker as green for path from the df, how can I do that? – Liza Jun 21 '17 at 02:09
  • Thanks a lottt.. Just a little change grp.iloc[0]/ grp.iloc[-1] should be wothin double square brackets like this: grp.iloc[ [0] ].plot(marker="o", x="px", y="py", ax=ax, color='r', legend=False) and grp.iloc[ [-1] ].plot(marker="o", x="px", y="py", ax=ax, color='g', legend=False) or else it will change the whole plot. – Liza Jun 21 '17 at 03:09
  • I am trying to move the legend to the right side of the chart with this piece of code: box = ax.get_position() ax.set_position([box.x0, box.y0, box.width * 0.8, box.height]) ax.legend(loc ='center left', bbox_to_anchor=(1, 0.5)) This works but in the legend its also including all the red and green markers separately even after making legend "False" for grp.iloc[[0]] and grp.iloc[[-1]]. – Liza Jun 21 '17 at 03:45
  • 1
    The answer to this is rather length, so I put edits to the answer above. – Gerges Jun 21 '17 at 05:08
  • I just realized, after adding this line of code ax.legend(ax.lines[::3], groups.groups.keys(), loc='center left', bbox_to_anchor=(1, 0.5)) the legend successfully moves right to the chart, but it's labelling is incorrect, like its labelling the wrong keys to the line. I am tried it on a simple data where I know which color line should be labelled with what key. Please let me know any suggestions to avoid this – Liza Jun 22 '17 at 13:40
  • oh, yes sorry the keys returned might not be in the order of the loop.. try making a new list and adding `key` to it as you go through the loop, then use that list for the labels. – Gerges Jun 22 '17 at 17:48