1

I am trying to plot windspeeds those are available in km/h. On the left I have the scale in km/h. Now I want to add a scale in bft on the right of the plot. The Bft scale is not linear. I would like to (manually) add the bft numbers (0-12) to specific windspeeds in km/h. eg. on the same height as 10.2km/h (left scale) I want have the number 2 written on the right scale (2bft = 7.4-13km/h)

does anyone know, if there is a way to add such a non-linear-scale manually?

Visual example: picture: on the top is how it looks currently, on the bottom how I would like it to look like

current code of this visual example:

#!/usr/bin/python
# -*- coding: utf-8 -*-
from datetime import datetime
from datetime import timedelta
from os import path
from os import system
import sys
from math import exp

#data processing
print("data-ready")
from matplotlib import use
use('Agg')
from matplotlib import pyplot as plt
import matplotlib.dates as mdates
print("plotlib ready")
plt.figure(1)
#multiple subplots for temprature, pressure, wind, humidity
#only wind on axs[1] is interesting here
fig, axs = plt.subplots(4, sharex=True, sharey=False)
axs[1].plot(xg, gusts,'bo')
axs[1].plot(x, winds,'black')
fig.set_figwidth(9)
fig.set_figheight(7)
axs[1].grid()
axs[1].minorticks_on()
axs[1].xaxis.set_minor_locator(mdates.HourLocator())
axs[1].grid(which='major', linestyle='-', color='lightgray')
axs[1].grid(which='minor', linestyle=':', color='lightgray')
axs[1].set_ylabel(u'Windstärke (km/h)\n[Punkte=Böen]')
axs12=axs[1]
ylim=(axs12.get_ylim())
axs12.axhspan(0, 1.85, facecolor='#00ffff', alpha=0.5)
axs12.axhspan(1.85, 7.41, facecolor='#00ff40', alpha=0.5)
axs12.axhspan(7.41, 12.96, facecolor='#80ff00', alpha=0.5)
axs12.axhspan(12.96, 20.37, facecolor='#ffff00', alpha=0.5)
axs12.axhspan(20.37, 29.36, facecolor='#ffc000', alpha=0.5)
axs12.axhspan(29.36, 40.74, facecolor='#ff6000', alpha=0.5)
axs12.axhspan(40.74, 51.86, facecolor='#ff0000', alpha=0.5)
axs12.axhspan(51.86, 62.97, facecolor='#ff0040', alpha=0.5)
axs12.axhspan(62.97, 75.93, facecolor='#ff0080', alpha=0.5)
axs12.axhspan(75.93, 140, facecolor='#ff00ff', alpha=0.5)
axs12.set_ylim(ylim)
#axs[x]...
print("sving")
plt.savefig("some_private_path/mfcPlot.png",bbox_inches = 'tight',pad_inches = 0)
print("done")
Simon
  • 15
  • 3
  • are you looking for [twinx](https://matplotlib.org/3.1.1/gallery/subplots_axes_and_figures/fahrenheit_celsius_scales.html#sphx-glr-gallery-subplots-axes-and-figures-fahrenheit-celsius-scales-py)? – CAPSLOCK May 25 '20 at 10:23
  • Does this answer your question? [matplotlib: overlay plots with different scales?](https://stackoverflow.com/questions/7733693/matplotlib-overlay-plots-with-different-scales) – CAPSLOCK May 25 '20 at 10:23
  • @Gio: sadly no, i already found those concepts, but I dont know how i could use them to add a non linear scale as shown in the picture for the same dataset without adding new data points – Simon May 25 '20 at 12:41
  • the non linear scale follows a specific function I imagine. Why don't you compute the corresponding value of bft, given the tick values in the original y_axis and then simply assign it to the second y_axis? – CAPSLOCK May 25 '20 at 12:51

2 Answers2

0

I may have found the solution myself:

matplotlib.axes.Axes.secondary_yaxis

but I will have to try it out to be sure. I will report my findings here as soon as I do in case some else has the same issues in the future

Edit:

I have not tested it as the other answer was successful for my usecase (have a look in the comments)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Simon
  • 15
  • 3
0

Not sure this is a proper answer but can't really write it as comment. Will cancel if needed.

My suggestion would be something on these lines:

yticks_val = ax.get_yticks(self, minor=False)

btf_scale_labels = someConversionFunction(yticks_val)

ax1 = ax.twinx()

ytick(yticks_val, btf_scale_labels)

def someConversionFunction(kmh_values)
    #do something
    return btf_values


EDIT

Did some googling. Following the Beaufort wikipedia page this should do the conversion trick:


kmh_values=[0,10,24]
def kmhToBft(np.array(kmh_values)):
    import numpy as np
    kmh_lim = np.array([2,5,11,19,28,38,49,61,74,88,102,117,10000])
    bft_scale = np.arange(13)
    bft_level = (kmh_values[:, None] <= kmh_lim)
    bft_values = np.nanmin(
                 np.where(bft_level, bft_scale, np.nan), axis=1)
    return bft_values
CAPSLOCK
  • 6,243
  • 3
  • 33
  • 56
  • thanks for the hint, I did not know about the yticks(..) function, i will have a look into that, maybe it will work – Simon May 25 '20 at 15:36
  • 1
    yep, the yticks(x,y) function was exactly what i was seraching for. x is an array of the values in km/h for which i want the bft values (y) to be prited on the right. Final solution is even more simple than you suggested: two arrays and a for loop. No functions, no calculations, no numpy – Simon May 25 '20 at 16:59
  • I’m glad it helped. – CAPSLOCK May 25 '20 at 21:18
  • 1
    but a word of warning to people trying to use this solution: for yticks(x,y), the first elemt of x has to be the bottom ylim of the original plot, the last element of x has to be the top of ylim. this seems to be the only way to have all values inbetween correctly scaled. – Simon May 27 '20 at 08:55
  • @Simon You are more than welcome to edit the answer so that other users can benefit from it. Given your comment, my approach would be to use `ax.get_ylim()` to retrieve the two edge values and add those to `yticks_val`. – CAPSLOCK May 27 '20 at 09:37