I have a dataset with 3 columns: DEPTH
, TVD_SCS_interp
, Gamma_ray
. I made a plot of DEPTH (on the left y-axis), TVD_SCS_interp (on the right y-axis) vs Gamma_ray (x axis) with the following code:
# Sample data (replace with your actual data)
data = {
'DEPTH': [100, 120, 140, 160, 180, 200, 220, 240],
'TVD_SCS_interp': [50.5, 60.2, 70.3, 80.7, 90.2, 100, 100.15, 120.9],
'GR_N': [150, 150, 120, 130, 140, 130, 110, 100]
}
ax1.plot("GR_N", "DEPTH", data = df, color = "#00be00").
ax1.set_ylim(bottom_depth, top_depth)
ax20.set_ylim(df.loc[df['DEPTH'] == bottom_depth, 'TVD_SCS_interp'].values[0], df.loc[df['DEPTH'] == top_depth, 'TVD_SCS_interp'].values[0])
I realized that secondary y (TVD_SCS) values do not correctly align with the corresponding primary y (DEPTH) values to the end of data points. This was because of data in ax2
has a different scale and arbitrary increments than ax1
's (where it has linear scale). For example, TVD_SCS value 100.15 was not in front of DEPTH value 220 like in the data I provided).
So, I found a partial solution to this problem and achieved the following result:
ax1 = plt.subplot(gs[0])
ax2 = ax1.twinx()
def depth_to_tvd(depth):
return df.loc[df['DEPTH'] == depth, 'TVD_SCS_interp'].values[0]
ax1.plot(df.Gamma_ray, df.DEPTH, color='b', label='DEPTH')
ax1.set_ylim(top_depth, bottom_depth)
ax2.set_ylim((ax1.get_ylim()))
ax1.yaxis.set_major_locator(ticker.MultipleLocator(10))
ax1.yaxis.set_minor_locator(ticker.MultipleLocator(1))
ax2.yaxis.set_major_locator(ticker.MultipleLocator(base=10))
ax2.yaxis.set_minor_locator(ticker.MultipleLocator(1))
ax2.set_yticklabels([Text(0, yval, f'{depth_to_tvd(yval):.2f}') for yval in ax1.get_yticks()])
I want to get tick positions for whole numbers (not decimals like in the picture) on the secondary y axis, and you see although I used
ax2.yaxis.set_major_locator(ticker.MultipleLocator(base=10))
ax2.yaxis.set_minor_locator(ticker.MultipleLocator(1))
it did not help me.
Could anyone help me to correctly align whole "TVD_SCS" values at the corresponding position of "DEPTH" and making the frequency of "TVD_SCS" ticks 10 as the primary y axis?
Finally, I figured out how to correctly align the secondary y axis values related to primary y axis, and displaying only 10th tick labels (2610, 2620, ..). In my dataset, TVD_SCS_interp
has no or very few whole numbers corresponding to DEPTH
. So, in
order to change the major tick positions to every 10 m and minor tick positions to every 1 m, I created whole values using np.arange
function.
from scipy.interpolate import interp1d
try: #if there is no DEPTH value for top_depth, find it by interpolation
s = np.ceil(df[df['DEPTH'] == top_depth]['TVD_SCS_interp'].values[0])
except:
s = np.ceil(Int_TVD_SCS(top_depth))
try: #the same here
e = np.floor(df[df['DEPTH'] == bottom_depth]['TVD_SCS_interp'].values[0]) + 1
except:
e = np.floor(Int_TVD_SCS(bottom_depth))
TVD_SCS_whole = np.arange(s, e)
Since, we get ticks from the primary axis and change its labels in the secondary axis, we need to get the corresponding DEPTH
values for those whole TVD_SCS
's (every 1.0 and 10.0 ticks)by interpolation:
Int_MD = interp1d(df["TVD_SCS_interp"], df["DEPTH"])
MD_interp = Int_MD(TVD_SCS_whole)
#Make MD_interp and TVD_SCS_whole a dataframe
....
#Join that to actual dataset
df = df.merge(df_MD_interp, on=['DEPTH','TVD_SCS_interp'], how='outer')
df = df.sort_values(by='DEPTH')
I will manually assign major and minor tick positions using ticker.FixedLocator())
function. Thus, I find major and minor tick values by the following code:
df['TVD_SCS_interp_mod'] = df['TVD_SCS_interp'] % 10
# Find major and minor ticks using vectorized operations
y_ticks_major = df.loc[df['TVD_SCS_interp_mod'] == 0, 'DEPTH'].tolist()
y_ticks_minor = df.loc[(df['TVD_SCS_interp_mod'] % 1 == 0) & (df['TVD_SCS_interp_mod'] != 0), 'DEPTH'].tolist()
ax1 = plt.subplot(gs[1])
ax20 = plt.subplot(gs[2])
ax201 = ax20.twinx()
ax1.plot("GR_N", "DEPTH", data = df, color = "#00be00", linewidth = 1, zorder=10, antialiased=True)
ax1.yaxis.set_major_locator(ticker.MultipleLocator(10))
ax1.yaxis.set_minor_locator(ticker.MultipleLocator(1))
def depth_to_tvd(depth):
return d.loc[d['DEPTH'] == depth, 'TVD_SCS_interp'].values[0]
ax20.set_ylim(bottom_depth, top_depth)
ax201.set_ylim((ax20.get_ylim()))
ax20.yaxis.set_major_locator(ticker.FixedLocator(y_ticks_major))
ax20.yaxis.set_minor_locator(ticker.FixedLocator(y_ticks_minor))
ax201.set_yticks(ax20.get_yticks())
ax201.set_yticklabels([Text(0, yval, f'{depth_to_tvd(yval):.0f}') for yval in ax20.get_yticks()])
ax201.yaxis.set_major_locator(ticker.FixedLocator(y_ticks_major))
ax201.yaxis.set_minor_locator(ticker.FixedLocator(y_ticks_minor))
#other plot functions (tick length,...)
...
The final plot is: