0

I am getting lost in different methods used in matplotlib.

I want to create a colour-coded scatter plot with a colorbar on the side and datetime on the x axis.

But depending on how I define my ax, I get different errors. Below is the core of my code:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.cm as cm
import matplotlib.dates as mdates

#.....loading files etc.

norm = mcolors.Normalize(vmin=0,vmax=1000)
timerange = pd.date_range(start='2015-01-01', end='2016-01-01', freq='30D')

### PLOTTING 
fig = plt.figure(figsize=(6.,5))
ax = fig.add_subplot(111)

for Af in Afiles:
    for index, row in Af.iterrows():
        time = pd.to_datetime(row['date'], format="%Y-%m-%d")
        plt.scatter(time, row['A'], c=row['z'], norm=norm, cmap=colormap,edgecolor='k', lw=0.8, s=80)

plt.xticks(timerange, rotation=90)
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d/%m/%Y"))
plt.xlabel('Time', fontsize=11, color='k')

clb = fig.colorbar(ax)       
clb.ax.set_title('Value', y=-0.125, fontsize=11)
clb.ax.invert_yaxis()

fig.tight_layout()

this produces AttributeError: 'AxesSubplot' object has no attribute 'autoscale_None'

but if I specify my ax as the scatter plot so that I can get my colour-coding working, I then have trouble with the axis formatter. Writing instead ax = plt.scatter generates AttributeError: 'PathCollection' object has no attribute 'xaxis'.

How can I have both the colorbar AND formatted axis ticks?

0

2 Answers 2

1

Don't call the scatter ax. (This overwrites the existinge axes ax.)
The colorbar expects as first argument a ScalarMappable (as e.g. the scatter). Since the scatters are all normalized, you can use it from the loop,

norm = plt.Normalize(...)
for bla in blubb:
    scatter = plt.scatter(..., norm=norm) 

Then,

clb = fig.colorbar(scatter)

The rest should stay the same.

Sign up to request clarification or add additional context in comments.

1 Comment

Of course! Silly me, thank you. This is embarrassing.
0

The basic idea is that you need to add an extra axis for the colorbar.

It's hard to know if this is an exact match, as you haven't provided a working example with data. But this may at least serve as a template.

First, some example data:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.cm as cm
import matplotlib.dates as mdates
from mpl_toolkits.axes_grid1 import make_axes_locatable

vmin = 0
vmax = 1000
timerange = pd.date_range(start='2015-01-01', end='2016-01-01', freq='30D')
N = len(timerange)

data = np.random.randint(vmin, vmax, size=N)
# z contains the colorbar values for each point
cmap = plt.get_cmap('Reds')
z = [cmap((x-vmin)/(vmax-vmin))[:3] for x in data]
df = pd.DataFrame({"value":data, "datetime":timerange, "z":z})

Now plot:

fig = plt.figure(figsize=(6.,5))
ax = fig.add_subplot(111)

plt.scatter(x=df.datetime.values, y=df.value.values, c=df.z)

ax.set_xticklabels(timerange, rotation=90)
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d/%m/%Y"))
ax.set_xlabel('Time')

Now add colorbar:

norm = mcolors.Normalize(vmin=vmin,vmax=vmax)
m = cm.ScalarMappable(cmap='Reds', norm=norm)
m.set_array([(x-vmin)/(vmax-vmin) for x in df.value.values])

divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.1)
clb = plt.colorbar(m, cax=cax)   

scatter plus colormap

3 Comments

Sorry, so for clarification, how would this work for my loop? How do I set the scalar mappable when I don't have a single dataframe but many? I need a common colorbar (common color scheme) for many dataframes with many different ranges of values. Note that my code for the colorbar (when setting ax = plt.scatter) worked perfectly as long as I kept numeric x axis. The problem appeared when I started playing with the axis formatting.
If you need a common colorbar, then you don't have many different ranges with respect to the colorbar, you'll have just one. Pick the min and the max of your full dataset to set the mappable. The loop can stay the same, I just made example data for one data frame to give a working example.
Having said that, it looks like ImportanceOfBeingErnest's answer is simpler, and I'll be the first to admit I do not have a good sense for elegance when it comes to Matplotlib. Give his approach a try!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.