0

I have created a 3D plot using csv data. I would like to log the z-axis however it appears that you can only change the labels, not the actual dimensions of the axis. Please see the script attached below:

enter code here

import matplotlib
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys
import csv
import bbox
csvFileName = sys.argv[0]
csvData = []
with open('Ben*PAR_nh.csv', 'r') as csvfile:
 csvReader = csv.reader(csvfile, delimiter=',')
 for csvRow in csvReader:
     csvData.append(csvRow)
csvData = np.array(csvData)
csvData = csvData.astype(float)
X, Y, Z = csvData[:,0], csvData[:,1], csvData[:,2]
#change vmin and vmax values for colorbar range
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
cb = ax.plot_trisurf(X, Y, Z, cmap='coolwarm', alpha=0.75)
#cb = ax.plot_trisurf(X, Y, Z, cmap='coolwarm', alpha=0.75, vmin=0, 
vmax=800)
ax.scatter(X, Y, Z, c='red', marker='o')
fig.colorbar(cb, shrink=0.5)
ax.set_title("First-year sea ice PAR")
ax.set_xlabel("SZA")
ax.set_ylabel("Ice thickness")
ax.set_zlabel("µmol m$^{-2}$ $^{s-1}$")
ax.view_init(azim=70, elev=30)
image_format = 'png' # e.g .png, .svg, etc.
image_name = 'test.eps'
plt.show()
fig.savefig(image_name, format=image_format, dpi=1200)

Thanks to Jamie's answer the plot now works with the Z axis now being logged - see below. The data range for the Z axis is from ~0.6 - 5e-07 resulting in the smallest values being difficult to see at this scale (0,1,4) - are there any suggestions for trying a different type of log?enter image description here

import matplotlib
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys
import csv
import bbox

csvFileName = sys.argv[0]
csvData = []
with open('parvalues.dat', 'r') as csvfile:
csvReader = csv.reader(csvfile, delimiter=',')
for csvRow in csvReader:
    csvData.append(csvRow)

csvData = np.array(csvData)
csvData = csvData.astype(float)
X, Y, Z = csvData[:,0], csvData[:,1], csvData[:,2]

#change vmin and vmax values for colorbar range
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
cb = ax.plot_trisurf*(X, Y, Z, cmap='coolwarm', alpha=0.75)

#cb = ax.plot_trisurf(X, Y, Z, cmap='coolwarm', alpha=0.75, 
vmin=0, vmax=800)
ax.scatter(X, Y, Z, c='red', marker='o')


fig.colorbar(cb, shrink=0.5)

ax.set_title("First-year sea ice PAR")
ax.set_xlabel("SZA")
ax.set_ylabel("Ice thickness")
ax.set_zlabel("µmol m$^{-2}$ $^{s-1}$")
ax.view_init(azim=70, elev=30)

image_format = 'png' # e.g .png, .svg, etc.
image_name = 'test.eps'
tight_layout()

plt.show()
fig.savefig(image_name, format=image_format, dpi=1200)
  • Please consider using more helpful / topical tags for your questions, for example in this case `python` and `matplotlib` would be more appropriate – jv-k Aug 08 '22 at 22:19

2 Answers2

1

Confused as to what you mean by 'log the axes', but I've made a work around where you can plot the log of the data, with a log scale, which I think is what you want to do, though of coure I don't know the dimensions of your data as its not shown so used random integers; This is my first help out on stack so have mercy if its awful code, seems to work though

import matplotlib
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys
import csv
#import bbox
import random


#I created random data here as I don't know what your data is like
X = random.sample(range(0,100),50)
Y = random.sample(range(0,100),50)
Z = random.sample(range(0,1000000), 50)


#ORIGINAL PLOT YOUR WORK
#change vmin and vmax values for colorbar range
fig = plt.figure(figsize=(20,10))
ax1 = fig.add_subplot(221, projection='3d')
cb = ax1.plot_trisurf(X, Y, Z, cmap='coolwarm', alpha=0.75)
cb = ax1.plot_trisurf(X, Y, Z, cmap='coolwarm', alpha=0.75, vmin=0, 
vmax=800)
ax1.scatter(X, Y, Z, c='red', marker='o')
fig.colorbar(cb, shrink=0.5)
ax1.set_title("First-year sea ice PAR")
ax1.set_xlabel("SZA")
ax1.set_ylabel("Ice thickness")
ax1.set_zlabel("µmol m$^{-2}$ $^{s-1}$")
ax1.view_init(azim=70, elev=30)
image_format = 'png' # e.g .png, .svg, etc.
image_name = 'test.eps'








### ISSUE SOLVING ###

### Create logZ data ####
logZ = np.log10(Z)


#Create the ticks you wish here, I assume orders of magnitude, so the (-2,2,1) here defines from 10^-2 to 10^5 in steps of orders of magnitude
zticks = [10**i for i in range(-2,6,1)]

#Log these ticks, as you want these on as the ticks on the graph in log form
logzticks = np.log10(zticks)


#Create tick labels based on the ticks themselves, these can be used if you want to label the actual data, but on a log scale, alternatively you can label the logged ticks ie
# a log axis ranging from -2 to 5 instead of 10^-2 to 10^5
def ticklabels(ticks):
    ticks_labels = []
    for i in ticks:
        ticks_labels.append(f'10^{np.log10(i)}')
    return ticks_labels

log_ztick_labels = ticklabels(zticks)


### NEW LOG PLOT ###
ax2 = fig.add_subplot(222, projection = '3d')

   
cb = ax2.plot_trisurf(X, Y, logZ, cmap='coolwarm', alpha=0.75)
cb = ax2.plot_trisurf(X, Y, logZ, cmap='coolwarm', alpha=0.75, vmin=0, 
vmax=800)
ax2.scatter(X, Y, logZ, c='red', marker='o')
fig.colorbar(cb, shrink=0.5)
ax2.set_title("First-year sea ice PAR (LOGSCALE EDIT)")
ax2.set_xlabel("SZA")
ax2.set_ylabel("Ice thickness")
ax2.set_zlabel("µmol m$^{-2}$ $^{s-1}$")
#Change z ticks and labels to change the logs here
ax2.set_zticklabels(log_ztick_labels)
ax2.set_zticks(logzticks)
ax2.view_init(azim=70, elev=30)

plt.show()

Resulting two graphs

0

There seems to be alot of known issues around 3D plots and log scales not playing nicely.. Matplotlib Issue 209 discusses this with suggested work arounds. This question also looks similar to what your trying to do?

From this answer the key seems to be plot the log data directly:

ax.scatter(X, Y, np.log10(Z))

Then apply the following function to update the ticker formatting on zaxis:

def log_tick_formatter(val, pos=None):
    """Reformat log ticks for display"""
    return f"$10^{{{int(val)}}}$"

ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))

Example with your plot setup and some dummy data: Output image / code example notebook