holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.69k stars 402 forks source link

Aspect on semi-log plots #996

Open vascotenner opened 7 years ago

vascotenner commented 7 years ago

The aspect parameter can be used to set the aspect of a plot:

%%opts Curve [ aspect=2]
hv.Curve((np.linspace(0.1,10 )))

download

However, when setting one of the axis to log, the aspect parameter is ignored:

%%opts Curve [logy=True, aspect=2]
hv.Curve((np.linspace(0.1,10 ),np.linspace(0.1,100 )))

download 1

philippjfr commented 7 years ago

I've noticed this as well and haven't yet been able to figure out what's going on here. Presumeably this expression doesn't work correctly with log axes:

axes.set_aspect(((1./axes.get_data_ratio()))/aspect)

vascotenner commented 7 years ago

When running for the first time, this warning appears:

/usr/local/lib/python2.7/dist-packages/matplotlib/axes/_base.py:1166: UserWarning: aspect is not supported for Axes with xscale=linear, yscale=log
  'yscale=%s' % (xscale, yscale))
philippjfr commented 7 years ago

Thanks, wonder what the correct way of setting log-linear plot aspects is then.

vascotenner commented 7 years ago

A solution inspired from stackoverflow answer:

def fixed_aspect_ratio_loglog(plot, element):
    '''
    Set a fixed aspect ratio on matplotlib loglog plots 
    regardless of axis units
    '''
    ratio = plot._aspect_param_value
    ax = plot.handles['axis']
    xvals,yvals = ax.axes.get_xlim(),ax.axes.get_ylim()

    xrange = np.log(xvals[1])-np.log(xvals[0])
    yrange = np.log(yvals[1])-np.log(yvals[0])
    ax.set_aspect(ratio*(xrange/yrange), adjustable='box')
%%opts Curve [logx=True, logy=True, aspect=1, final_hooks=[fixed_aspect_ratio_loglog_hv]]
hv.Curve((np.linspace(1,10,10), np.linspace(1,10,10)**10))

download

philippjfr commented 7 years ago

Assigned myself, will look into it.

thoth291 commented 7 years ago

Hi, @philippjfr .

I'm sorry for bothering.

Any progress on this?

I did this

import numpy as np
def fixed_aspect_ratio_loglog(plot, element):
    '''
    Set a fixed aspect ratio on matplotlib loglog plots 
    regardless of axis units
    '''
    ratio = plot.aspect
    ax = plot.handles['axis']
    xvals,yvals = ax.axes.get_xlim(),ax.axes.get_ylim()
    print xvals,yvals
    xrange=xvals[1]-xvals[0]
    #if ax.get_xaxis().get_scale()=='log':#or linear 
    if plot.logx:
        xrange = np.log(xvals[1])-np.log(xvals[0])
    yrange=yvals[1]-yvals[0]
    #if plot._logy_param_value:
    #if ax.get_yaxis().get_scale()=='log':
    if plot.logy:
        yrange = np.log(yvals[1])-np.log(yvals[0])
    print ax.get_aspect()
    print xrange,yrange,ratio
    ax.set_aspect(ratio*(xrange/yrange), adjustable='box')
    print ax.get_aspect()

plot=curve(plot=dict(logx=True,aspect=1,final_hooks=[fixed_aspect_ratio_loglog]),style=dict(width=800,height=600))
plot

And got this

(0.0001, 10.0) (0.0, 89.086297565778608)
auto
11.512925465 89.0862975658 1
0.129233403784

And these warnings:

.../matplotlib/axes/_base.py:1292: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  if aspect == 'normal':
.../matplotlib/axes/_base.py:1297: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  elif aspect in ('equal', 'auto'):
.../matplotlib/axes/_base.py:1404: UserWarning: aspect is not supported for Axes with xscale=log, yscale=linear
  'yscale=%s' % (xscale, yscale))

The image is still square: image

Thank you for your help!

thoth291 commented 7 years ago

Here is what I'm actually trying to do but implemented in matplotlib:

%matplotlib inline
from matplotlib import pyplot as plt
plt.figure(figsize=(20,10))
plt.semilogx(times, freqs)
plt.title('all of them')
plt.grid(True)

image

philippjfr commented 7 years ago

Keeping this open, we have a fix for loglog plots now, but not semilog.

NGillet commented 5 years ago

A temporary solution is to set the figure size to force it to be square:

fig, ax = plt.subplots(  )
ax.set_yscale("log", nonposy='clip')
...
fig_size = fig.get_size_inches()
fig.set_size_inches( fig_size[1]*1.1, fig_size[1]*1.1 )

the 1.1 scaling is for avoiding cuts in the labels, should be adapted for case to case.

seanysull commented 5 years ago

I have a log scale on my y-axis and wish to stretch out the x-axis, is there a workaround for achieving this?

rabernat commented 5 years ago

Just hit this issue. Would be great to see a fix.

jadolfbr commented 4 years ago

@philippjfr - You say you have a fix for loglog plots, but I still cannot get an equal aspect ratio for them. Anytime I attempt to set it - it squishes the X axis while elongating the y axis. Otherwise, its pretty much the opposite.

plt.scatter(n_data[x_group], n_data[y_group], marker='D', s=23) 

axes = plt.gca()
axes.set_xlim([min_x,max_x])
axes.set_ylim([min_y,max_y])

plt.plot([min_x, max_x], [min_y, max_y], 'k-')
plt.loglog()
plt.gca().set_aspect('equal', adjustable='box')

Results in something like this:

image

Am I doing something inherently wrong with trying to get an equal aspect ratio? Is there any workaround? I've also tried setting the xscale and yscale to logs, same thing.

Similar code, but for non log/log gives something like this: image