Skip to content

Loading…

3D scatter plots don't work in logscale #209

Open
ddale opened this Issue · 26 comments
@ddale
Matplotlib Developers member

Original report at SourceForge, opened Fri Jun 10 12:29:24 2011

If you enable a log scale when doing a 3D scatter plot, nothing is created and the program crashes. Attached is the error output.

You can easily reproduce this by taking the example scatter3d_demo.py and adding the line "ax.set_xscale('log')".

SourceForge History

  • On Fri Jun 10 12:29:40 2011, by qulogic: File Added: 414517: mpl-3d-log-error.txt
@WeatherGod WeatherGod was assigned
@WeatherGod
Matplotlib Developers member

Confirmed, however the problem is more nuanced than this. First, the problem does not error-out if one does ax.w_xscale.set_scale('log'), however, this does not set the scale properly because the projection never takes it into account.

Calling ax.set_xscale (or ax.set_yscale) appears to trigger some other stuff that leads to an error for displaying the coordinates in the corner. ax.set_zscale does not appear to exist. However, given that scaling does not appear to work properly right now, I am not inclined to add it.

@gllort

Problem seems to persist. I'm setting:

ax.xaxis.set_scale('log')
ax.yaxis.set_scale('log')
ax.zaxis.set_scale('log')

And then the ticks of the axes change to logarithmic scale:

scatter3d_log

While I'd expect the ticks to be equidistant and the data re-scaled, as it happens when using 2D plotting.

@WeatherGod
Matplotlib Developers member

Well... it is better than it used to be. The issue is that there is a lot of code in mplot3d that just completely ignores the projection information of the axes. While significant progress has been made in axes3d.py, it is axis3d.py that still needs a lot of TLC.

@ghost

@gllor or @WeatherGod t, I am facing the same problem (and hence is happy to find this discussion).. could you finally got it to work? Getting the 3d plot with data logarithmically rescaled??

@gllort

@Panchie I'm sorry I cannot shed more light into the issue, I didn't get it working and didn't find any solution googling around.

@ibell

+1 on this issue, stuck with exactly this problem

@r2park

+1

@troyrock

I would also like to see this issue resolved.

@BenjAbecassis

The problem is till there apparently. It is a big issue for me. I hope someone could resolve it soon.

@jstypka

+1

@hyperbowl

any progress 3 years later? set_(x,y,z)scale still just sets axis labels, not axis scaling.

@WeatherGod
Matplotlib Developers member
@tacaswell tacaswell added this to the unassigned milestone
@1minus1

+1

@OceanWolf

-1, just kidding, the log won't work with that :wink:.

The problem described sounds like a very simple problem, I mean as all the axes exist orthogonal to one another the problem should in theory exist no different for 3d as for 2d or for that matter any number of plotable dimensions. The fact that this problem has lasted so long tells me that the code says otherwise, in which case I imagine, as with a lot of the codebase, it needs a refactor, the good news I like doing.

The biggest problem though lies in the amount that needs refactoring. Starting at the base and working our way up, with bringing tools out of the backend (MEP 22) finished and general backend refactoring (MEP 27) on its way, I guess the main plotting functions will come next.

As @WeatherGod says, if anyone feels like jump in and looking through the code, then feel free (I only jumped in about a month or two ago myself), we can give guidance if needed and it works a lot better than +1s :wink:.

@tommyhlw

how hard would it be to make a workaround which prevents all set_scale('log') in 3D and calls a module which calculates the affected data array log (e.g. np.log(data) ) and changes the ticks labels to decimal power?
As long the bug exists a patch like this could help.

I will try to add an extra module in my 3D Scripts, where the data and fixed scale values will be modified, sth like this:

def scalelog(scale,scalename):
newscale = []
newscale = np.power(10,scale)
if scalename=='x':
plt.xticks(scale,newscale)
if scalename=='y':
plt.yticks(scale,newscale)
if scalename=='z':
plt.zticks(scale,newscale)

scalelog([1,2,3,4],'z')
ax.scatter(xs., ys, np.log(zs), c=c, marker=m)

@sbneto

+1

@ramidas

+1

@Teque5

4 year old bug

workaround:

Z = np.log10(Z)
ax.plot_surface(X,Y,Z,cmap=cm.jet)
zticks = [1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10]
ax.set_zticks(np.log10(zticks))
ax.set_zticklabels(zticks)
@tommyhlw

@Teque5
that's kind of the same I wanted to solve this issue.
But neither with my way nor yours I was able to show log date in 3d plot correctly.
Could you post a full example?

@Teque5

@tommyhlw
Toggle enablelog to see the difference.
Note that if you are working with time data things can be tricky and you might want to put it into unix epoch instead of messing with various datetime objects.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

enablelog = True

snr=15
fig = plt.figure(figsize=(12,12))
ax = fig.gca(projection='3d')

realsnr = 10**(snr/10)
B,T = np.mgrid[100e3:10e6:100e3, 1:60:1]
C = .55 / (np.sqrt(2) * B * np.sqrt(B * T * realsnr) )
if enablelog: C = np.log10(C)
surf = ax.plot_surface(
    B/1e6, T, C, cmap='rainbow',cstride=1,
    rstride=1,linewidth=0,antialiased=False)
ax.set_xlabel("Bandwidth MHz")
ax.set_ylabel("Integration Time")
ax.set_zlabel("Cramer-Rao Lower Bound")
if enablelog:zticks = [1e-13,1e-12,1e-11,1e-10,1e-9]
if enablelog:ax.set_zticks(np.log10(zticks))
if enablelog:ax.set_zticklabels(zticks)
plt.show()

To those watching from the bleachers, ideally we would just have to add ax.set_zscale('log') to achieve this effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.