Pandas: some markers missing from legend

Created on 22 Dec 2016  路  18Comments  路  Source: pandas-dev/pandas

Code Sample, a copy-pastable example if possible

# Your code here
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.randn(8, 3), 
                  columns=['A', 'B', 'C'])
ax = df.plot (y=['A'], marker='x', linestyle='solid')
df.plot (y=['B'], marker='o', linestyle='dotted', ax=ax)
df.plot (y=['C'], marker='<', linestyle='dotted', ax=ax)
plt.grid()
plt.show()


Problem description

The legend is missing some markers.
If I add
plt.legend()
then they show up.

See attached
figure1.pdf

[this should explain why the current behaviour is a problem and why the expected output is a better solution.]

Expected Output

Output of pd.show_versions()

Paste the output here pd.show_versions() here

INSTALLED VERSIONS

commit: None
python: 3.5.2.final.0
python-bits: 64
OS: Linux
OS-release: 4.8.14-300.fc25.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.19.1
nose: 1.3.7
pip: 9.0.1
setuptools: 31.0.0
Cython: 0.25.2
numpy: 1.12.0rc1
scipy: 0.18.1
statsmodels: None
xarray: None
IPython: 5.1.0
sphinx: 1.5
patsy: None
dateutil: 2.6.0
pytz: 2016.10
blosc: None
bottleneck: None
tables: 3.3.0
numexpr: 2.6.1
matplotlib: 2.0.0rc2
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: 4.5.1
html5lib: 0.999
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.8
boto: None
pandas_datareader: None

Bug Visualization

Most helpful comment

No, I think this is probably just a pandas issue. In matplotlib you need to call legend explicitly in any case. It is pandas that gives the convenience of trying to do this for you.

Eg in this pure matplotlib example, only after calling the first ax.legend(), the legend is drawn. When adding another line, you need to call it again to update the legend:

fig, ax = plt.subplots()
ax.plot(df.index, df.A, marker='x', linestyle='solid')
ax.legend()
ax.plot(df.index, df.B, marker='o', linestyle='dotted')
ax.legend()

All 18 comments

@nbecker Thanks for the report. Can confirm this on master. The legend drawing should be retriggered somehow after adding the additional lines (although I am not sure why the lines get added automatically, but the markers not)

The 'ax' object let us draw all the plots in the same figure. If we draw each plot separately like this

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.randn(8, 3), 
                  columns=['A', 'B', 'C'])
ax = df.plot (y=['A'], marker='x', linestyle='solid')
df.plot (y=['B'], marker='o', linestyle='dotted')
df.plot (y=['C'], marker='<', linestyle='dotted')
plt.show()

We get the desired result. We need not add plt.legend().
@nbecker @jorisvandenbossche

@Vutsuak16 Yes, that is correct, but then you get separate figures. The point here is to have them on a single figure. And it is in that case that only the first one gets a correct legend.

Well let me have a look on it.
Should I hack into the matplotlib or pandas codebase ? Maybe there is some kind of conflict ?
@jorisvandenbossche

Should I hack into the matplotlib or pandas codebase ?

In pandas I think. We have some logic for when to redraw the legend. See pandas/tools/plotting.py.

No, I think this is probably just a pandas issue. In matplotlib you need to call legend explicitly in any case. It is pandas that gives the convenience of trying to do this for you.

Eg in this pure matplotlib example, only after calling the first ax.legend(), the legend is drawn. When adding another line, you need to call it again to update the legend:

fig, ax = plt.subplots()
ax.plot(df.index, df.A, marker='x', linestyle='solid')
ax.legend()
ax.plot(df.index, df.B, marker='o', linestyle='dotted')
ax.legend()

related to #14563. Current legend-redrawing logic doesn't work well in specific condition.

This also seems to work nicely and would save time/code on multiple plots,

fig, ax = plt.subplots()
ax.plot(df.index, df.A, marker='x', linestyle='solid')
ax.plot(df.index, df.B, marker='o', linestyle='dotted')
ax.plot(df.index, df.C, marker='>', linestyle='solid')
# more plots here...

ax.legend()

Any updates on this?

Still open. Are you interested in working on it @et2010?

I would like to but I don't know where to start @TomAugspurger

I didn't figure out how to fix this. But my friend @ashfinal gave me a work around for this:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.randn(8, 3), 
                  columns=['A', 'B', 'C'])
ax = df.plot (y='A', marker='x', linestyle='solid')
df.plot (y='B', marker='o', linestyle='dotted', ax=ax, subplots=True)
df.plot (y='C', marker='<', linestyle='dotted', ax=ax, subplots=True)
plt.grid()
plt.show()

Add keyword argument subplots=True and the marker in legend will show as expected.

Also, one can modify the default rcParams to include marker styles using the cycler API from matplotlib. The benefit of doing so is that the legend (with name) could be formatted automatically without further tinkering.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from cycler import cycler

df = pd.DataFrame(np.random.randn(8, 3), columns=['A', 'B', 'C'])
df.columns.name = 'name'  # <=== legend description

marker_styles = cycler('marker',
                       ['o', 'x', 's', 'D', '*', '+', 'h', 'p', '^', 'P'])
color_styles = plt.rcParams['axes.prop_cycle']
plt.rcParams['axes.prop_cycle'] = marker_styles + color_styles

fig, ax = plt.subplots()
df.plot(ax=ax)

plt.grid()

image

I ran into the same situation. When my plot style was seaborn-whitegrid, I could not view the indications displayed by plt.legend() but when I changed my plot style using plt.style.use('classic') then I could see the correct output. I hope that helps!

i think this has been fixed, and i cannot reproduce it on master, seems to be fixed by #27808

Screen Shot 2020-01-04 at 6 03 19 PM

so i think this can be closed @TomAugspurger @jreback

do we have sufficient tests for this already; if not can u add one

well, i have one in previous PR which fixed it,

@jreback how about I making tests for this issue and #14563 separately in one PR? and then close these two issues? they are very similar, def same type of issue.

Was this page helpful?
0 / 5 - 0 ratings