-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
SymLog scale has too few ticks #17402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I imagine this will be quite tricky to fix, so in case someone is in urgent need of extra ticks, here is a quick and dirty code that works quite well for me: class MajorSymLogLocator(SymmetricalLogLocator):
def __init__(self):
super().__init__(base=10., linthresh=1.)
@staticmethod
def orders_magnitude(vmin, vmax):
max_size = np.log10(max(abs(vmax), 1))
min_size = np.log10(max(abs(vmin), 1))
if vmax > 1 and vmin > 1:
return max_size - min_size
elif vmax < -1 and vmin < -1:
return min_size - max_size
else:
return max(min_size, max_size)
def tick_values(self, vmin, vmax):
if vmax < vmin:
vmin, vmax = vmax, vmin
orders_magnitude = self.orders_magnitude(vmin, vmax)
if orders_magnitude <= 1:
spread = vmax - vmin
exp = np.floor(np.log10(spread))
rest = spread * 10 ** (-exp)
stride = 10 ** exp * (0.25 if rest < 2. else
0.5 if rest < 4 else
1. if rest < 6 else
2.)
vmin = np.floor(vmin / stride) * stride
return np.arange(vmin, vmax, stride)
if orders_magnitude <= 2:
pos_a, pos_b = np.floor(np.log10(max(vmin, 1))), np.ceil(np.log10(max(vmax, 1)))
positive_powers = 10 ** np.linspace(pos_a, pos_b, int(pos_b - pos_a) + 1)
positive = np.ravel(np.outer(positive_powers, [1., 5.]))
linear = np.array([0.]) if vmin < 1 and vmax > -1 else np.array([])
neg_a, neg_b = np.floor(np.log10(-min(vmin, -1))), np.ceil(np.log10(-min(vmax, -1)))
negative_powers = - 10 ** np.linspace(neg_b, neg_a, int(neg_a - neg_b) + 1)[::-1]
negative = np.ravel(np.outer(negative_powers, [1., 5.]))
return np.concatenate([negative, linear, positive])
else:
pos_a, pos_b = np.floor(np.log10(max(vmin, 1))), np.ceil(np.log10(max(vmax, 1)))
positive = 10 ** np.linspace(pos_a, pos_b, int(pos_b - pos_a) + 1)
linear = np.array([0.]) if vmin < 1 and vmax > -1 else np.array([])
neg_a, neg_b = np.floor(np.log10(-min(vmin, -1))), np.ceil(np.log10(-min(vmax, -1)))
negative = - 10 ** np.linspace(neg_b, neg_a, int(neg_a - neg_b) + 1)[::-1]
return np.concatenate([negative, linear, positive])
def symlogfmt(x, pos):
return f'{x:.6f}'.rstrip('0') And then when you do the plot, add this: ax.yaxis.set_major_locator(MajorSymLogLocator())
ax.yaxis.set_major_formatter(FuncFormatter(symlogfmt)) This solution is still broken in some cases, hardcoded to base 10 and threshold 1 (doesn't it make more sense than 2 as a default?), but works better than default: |
This behaves slightly better (I think? it is hard to tell with the randomness) on master. There was recently a bunch of work with the log formatters and locators to make sure we always had a reasonable number of ticks on the screen, the fix here would likely be to see if any of those code patterns / logic can be adapted. |
I labeled this a "good first issue" because the changes required are likely to be isolated to the locator / formatter for symlog, but medium difficulty as it will involve reaching a consensus on what the "right" behavior is. |
Doesn't look like anyone is working on this, so I'd like to take this up. |
Hello, In fact, we are working on this issue for few weeks. And we assume having fixed the issue. Here is the code that we have changed (in the class SymetricalLogLocator() - function tick_values() ) illustrated by an example with 16 graphs. We just had a condition at the end of the function. Thus, we are going to do a pull request.
|
Hello, May I ask when this will be available? Sorry for my lack of knowledge, can I make this somehow work locally in my project? If so, how? |
@AxelMKlein This seems to be dead. Maybe @QuLogic has it in mind for v3.6. In the meantime, I think you can take @Schmutsi 's code and set your ticks using it. Something like |
Bug report
Bug summary
SymLog scale is exactly what I need to display various reinforcement learning related metrics as they can be both positive and negative, interesting mainly in the -10 to 10 range, and frequently explode to very large values only to come back "to sanity" later.
Unfortunately, if things go well and metrics don't go insane, current SymLog graphs have too few ticks and is not possible to understand what am I looking at. Here is a synthetic example that illustrates the issue quite well:
Code for reproduction
Actual outcome
Expected outcome
It would be awesome to get more ticks and nicer labels!
Matplotlib version
matplotlib 3.1.1
I think the versions of all the remaining stuff are irrelevant, as this is an issue in the SymmetricalLogLocator class, which is built on an assumption that does not hold in my case:
The text was updated successfully, but these errors were encountered: