6

I have a series of subplots with red and blue markers, I am most interested in the red markers so want to bring them to the front of the plot:

enter image description here

The data structure is like this:

            SzT     Pcp     Pcp_3day    Pcp_7day    Pcp_10day   Pcp_14day   Pcp_21day   Pcp_28day
date        
2017-12-04  0.0     8.382   19.304      21.082      40.132      40.132      42.418      71.374
2017-12-05  0.0     12.192  20.574      33.020      42.164      52.324      52.578      81.534
2017-12-06  0.0     1.016   21.590      33.020      34.290      53.340      53.594      82.550
2017-12-07  0.0     12.700  25.908      45.466      46.990      66.040      66.040      95.250
2017-12-08  0.0     5.080   18.796      50.292      51.816      71.120      71.120      88.900

The colours are determined by the value of 'SzT' that each data point belongs to, which is either 1 or 0 (though in the above only '0' is shown). I constructed this with the code below:

colors = {0 : 'b',
          1 : 'r'}


fig = plt.figure(figsize=(20,10))
ax = fig.add_subplot(111)
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)

c = [colors[i] for i in RGDFT8mm['SzT']]
m = [marker[i] for i in RGDFT8mm['SzT']]
ax1.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_3day'], c=c)
ax2.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_7day'], c=c)
ax3.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_14day'], c=c)
ax4.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_28day'], c=c)

ax.set_title('Daily Rainfall vs antecedent rainfall from Rain Gauges 2001-2017')
ax.set_xlabel('Daily Rainfall (mm)')
ax.set_ylabel('Antecedent rainfall (mm)')
ax.set_yticklabels([])
ax.set_xticklabels([])

ax1.set_title('3 Day')
ax2.set_title('7 Day')
ax3.set_title('14 Day')
ax4.set_title('28 Day')

I can't find any information that is helpful elsewhere. Any ideas out there?

Thanks!

UPDATE: Apologies for the poor original structure, I have added the structure of the data above FYI.

SHV_la
  • 875
  • 1
  • 10
  • 14
  • 2
    This is not a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) but it seems you are looking for [zorder](https://matplotlib.org/gallery/misc/zorder_demo.html) – Mr. T Nov 09 '18 at 07:43

2 Answers2

2

At first it's quite difficult to say sth concrete without knowing the structure of your data in the dataframe, so please consider posting e.g. RGDFT8mm.head()

That said, I see at least from your code that you have mixed red and blue data in one dataframe without grouping (=separating) it before scatter plotting. Therefore one scatter command contains both colors making it impossible to get one color in the foreground.
If you restructure so that each scatter command only plots a single color, every scatter will be plotted on top of the previous one, and besides that, you can use the zorder kwarg to define the layer of each dataset at your own will.

For grouping you can use sth like RGDFT8mm.groupby('SzT') - however, to give useful hints from here on I would rather wait to know your dataframe structure exactly.
But my first guess would be:

for grpname, grpdata in RGDFT8mm.groupby('SzT'):
    ax1.scatter(grpdata['Pcp'], grpdata['Pcp_3day'])
    ax2.scatter(grpdata['Pcp'], grpdata['Pcp_7day'])
    ax3.scatter(grpdata['Pcp'], grpdata['Pcp_14day'])
    ax4.scatter(grpdata['Pcp'], grpdata['Pcp_28day'])

Edit Examples for clarification

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

data = lambda n: np.random.lognormal(sigma=.5, size=n)
np.random.seed(42)
df = pd.DataFrame({'Pcp': data(500), 'Pcp_3day': data(500), 'SzT': (np.random.random(500)>.9).astype(int)})
print(df.head())

fig, axs = plt.subplots(2, 2, sharex=True, sharey=True)

szt_hi = df.SzT > 0

axs[0, 0].set_title('plot red before blue')
axs[0, 0].scatter(df.loc[szt_hi, 'Pcp'], df.loc[szt_hi, 'Pcp_3day'], c='r', label='SzT=1')
axs[0, 0].scatter(df.loc[~szt_hi, 'Pcp'], df.loc[~szt_hi, 'Pcp_3day'], c='b', label='SzT=0')
axs[0, 0].legend()

axs[0, 1].set_title('plot blue before red')
axs[0, 1].scatter(df.loc[~szt_hi, 'Pcp'], df.loc[~szt_hi, 'Pcp_3day'], c='b', label='SzT=0')
axs[0, 1].scatter(df.loc[szt_hi, 'Pcp'], df.loc[szt_hi, 'Pcp_3day'], c='r', label='SzT=1')
axs[0, 1].legend()

colors = {0 : 'b', 1 : 'r'}
layer = {0: 1, 1: 0}
axs[1, 0].set_title('plot by looping over groups\n(leading to blue first here)')
for i, (n, g) in enumerate(df.groupby('SzT')):
    axs[1, 0].scatter(g.Pcp, g.Pcp_3day, c=colors[i], label='SzT={}'.format(n))
axs[1, 0].legend()

axs[1, 1].set_title('plot by looping over groups \n(leading to blue first here)\nwith manipulating zorder')
for i, (n, g) in enumerate(df.groupby('SzT')):
    axs[1, 1].scatter(g.Pcp, g.Pcp_3day, c=colors[i], zorder=layer[i], label='SzT={}'.format(n))
axs[1, 1].legend()

plt.show()    

enter image description here


...to print legend less times one could loop over all axes like

for a in axs.flatten():
    a.legend()

after plotting all subplots.

However, in your case in contrast to my examples, your legends would all be the same, so that one legend for the whole figure would be better. For this just use

fig.legend()

modifiable with the same parameters like axis legends.

SpghttCd
  • 10,510
  • 2
  • 20
  • 25
  • Hello, thank you very much for your response! I have updated the question now (sorry I asked it at the end of a long day so wasn't as descriptive as I should have been). Are you able to expand on what you have said so far? For instance, are you able to advise how I would set which set of points (red or blue) would appear at the front? – SHV_la Nov 09 '18 at 15:39
  • So far, this would lead to the last plotted being on top as I said. Think of opaque paint, just the same here. But if you want to change this, add a `zorder` kwarg to the scatter commands. The higher the value the more on the top as far as I remember. – SpghttCd Nov 09 '18 at 15:54
  • Thanks, and sorry if I am being dull, but what I don't understand is where I should be placing the zorder command in order to specify which should go on top. From the example below I gather that each line is receiving a command within the plot command, but if I did the same with your suggestion above, surely I would apply the zorder to both classes as it is being looped... `plt.plot(x, np.sin(x), label='zorder=10', zorder=10) # on top; plt.plot(x, np.sin(1.1*x), label='zorder=1', zorder=1) # bottom; plt.plot(x, np.sin(1.2*x), label='zorder=3', zorder=3)` – SHV_la Nov 09 '18 at 16:19
  • Correct, so if you want a `zorder` which changes over several loop iterations, you just have to give it a value which depends e.g. on the loop counter instead of a constant. – SpghttCd Nov 10 '18 at 00:18
  • There's a reason there is 'Legend' written so frequently in the script... legendary, thank you so much – SHV_la Nov 12 '18 at 11:26
  • You're welcome, glad when it helps. Regarding legends, please see latest edit. – SpghttCd Nov 12 '18 at 16:05
1

Just set the alpha of the scatter points. Something like the following code. Of course, you can play with the alpha values.

colors = {0 : (0, 0, 1, 0.3),
          1 : (1, 0, 0, 1.0)}


fig = plt.figure(figsize=(20,10))
ax = fig.add_subplot(111)
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)

c = [colors[i] for i in RGDFT8mm['SzT']]
m = [marker[i] for i in RGDFT8mm['SzT']]

ax1.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_3day'], c=c)
ax2.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_7day'], c=c)
ax3.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_14day'], c=c)
ax4.scatter(RGDFT8mm['Pcp'], RGDFT8mm['Pcp_28day'], c=c)

ax.set_title('Daily Rainfall vs antecedent rainfall from Rain Gauges 2001-2017')
ax.set_xlabel('Daily Rainfall (mm)')
ax.set_ylabel('Antecedent rainfall (mm)')
ax.set_yticklabels([])
ax.set_xticklabels([])

ax1.set_title('3 Day')
ax2.set_title('7 Day')
ax3.set_title('14 Day')
ax4.set_title('28 Day')

Also just a suggestion: use plt.subplots() and zip when plotting multiple plots. I find that neat and helpful. Check this

anotherone
  • 680
  • 1
  • 8
  • 23
  • Hello, thanks so much for your comment. Unfortunately this does not seem to work for me. I receive an erro saying 'TypeError: alpha must be a float or None', even when I change both specified alpha values to floats – SHV_la Nov 09 '18 at 15:34
  • @SHV_la have you changed alpha when you are calling it inside the scatter method. check the edit i made to my post. it should work, in my understanding. – anotherone Nov 09 '18 at 16:16
  • haha, so, I have done exactly what you have editted now it's giving me this 'TypeError: float() argument must be a string or a number, not 'list'' I really don't understand why it's saying that... – SHV_la Nov 09 '18 at 16:25
  • @SHV_la can you try to recent edit i made? found out the matplotlib scatter does not accept list for alpha, but it seems that we can set the RGBA value, so I gave those values as a tuple. also check this answer https://stackoverflow.com/questions/24767355/individual-alpha-values-in-scatter-plot . seems like this time it will work. :\ – anotherone Nov 09 '18 at 18:09
  • that is a really useful thing to be able to do, so thank you, but it doesn't achieve quite what I am looking for, as the red markers are still behind the blue... – SHV_la Nov 09 '18 at 21:22
  • :( . but if you decrease the alpha by a lot, then it almost becomes transparent. also choosing lets say shades of a same color, say gray, may mimic the effect you are after (https://jakevdp.github.io/PythonDataScienceHandbook/04.03-errorbars.html). but it seems zorder is the way to go for you – anotherone Nov 09 '18 at 21:28
  • yeah that's true, I will give that a go and will look at zorder next! Thank you so much for your efforts :D really appreciate it – SHV_la Nov 12 '18 at 11:20