matplotlibtransformscalelogarithm

Matplotlib does not consistently transform plotted elements with plt.xscale('log')


When plotting data, vertical line gets incorrectly transformed by matplotlib. With linear x axis, my curve and the vertical line pointing to a specific location on the curve match perfectly. Vertical line touching curve as expected After plt.xscale('log') the end point of the vertical line is not on my curve anymore. Second screenshot after log transform

import numpy as np
import matplotlib.pyplot as plt

# Generate example data: x values from 1 to 16 (log scale)
x = np.array([1, 2, 4, 8, 16])
y = np.array([0.1, 0.2, 0.3, 0.6, 0.9])

# Perform linear interpolation to find where y crosses 0.5
crossings = np.where(np.diff(np.sign(y - 0.5)))[0]

if len(crossings) > 0:
    crossing_index = crossings[0]
    x1, x2 = x[crossing_index], x[crossing_index + 1]
    y1, y2 = y[crossing_index], y[crossing_index + 1]
    
    # Linear interpolation to find exact x for y = 0.5
    x_nd50 = x1 + (0.5 - y1) * (x2 - x1) / (y2 - y1)
    
    print(f"Interpolated x (ND50) = {x_nd50}")

# Plot the data with a logarithmic x scale
plt.plot(x, y, label="Data", color="blue")

# Plot the vertical line at the interpolated ND50 value
plt.plot([x_nd50, x_nd50], [0, 0.5], color='red', linestyle="--", alpha=0.7)
plt.scatter(x_nd50, 0.5, color='red', marker='x', alpha=0.7)

# First screenshot taken at this point!

# Set x-axis to log scale (log2)
plt.xscale('log')

# Second screenshot taken at this point!

# Show the plot
plt.xlabel('Log-scaled X')
plt.ylabel('Y')
plt.legend()
plt.show()


Solution

  • You calculate the x-position using linear interpolation. But on a log-plot, linear interpolation doesn't work.

    In fact, Matplotlib shows straight lines on the log-plot, but these are not the same straight lines as on the linear-plot: they would be curved if they were the same, and vice versa. Matplotlib just tries its best, having no extra information about the curve shape between the points. (And it assumes you picked log-space so that curves would show as straight lines, so it defaults to straight lines between the points.)

    Hence you need to calculate the x-position using logarithm x-data. You can solve it as follows:

    [...]
    
    xlog = True
    # Perform linear interpolation to find where y crosses 0.5
    crossings = np.where(np.diff(np.sign(y - 0.5)))[0]
    
    if len(crossings) > 0:
        crossing_index = crossings[0]
        x1, x2 = x[crossing_index], x[crossing_index + 1]
        y1, y2 = y[crossing_index], y[crossing_index + 1]
    
        if xlog:
            x1 = np.log10(x1)
            x2 = np.log10(x2)
        # Linear interpolation to find exact x for y = 0.5
        x_nd50 = x1 + (0.5 - y1) * (x2 - x1) / (y2 - y1)
        if xlog:
            x_nd50 = 10**x_nd50
        print(f"Interpolated x (ND50) = {x_nd50}")
    
    [...]
    
    # Set x-axis to log scale (log2)
    if xlog:
        plt.xscale('log')
    
    [...]