You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python自定义Matplotlib坐标轴缩放以清晰展示环形测量数据?

Custom Axis Scaling for Circular Plot in Matplotlib

Absolutely! You can achieve this custom axis scaling in Python with Matplotlib by creating a custom scale transformation—it’ll let you compress the inner -145 to 145mm range while zooming in on the critical 145–150mm (and -145–-150mm) qualified interval. Here’s a tailored, step-by-step solution:

Step 1: Define the Custom Scale Transformation

Matplotlib lets you build custom scales by subclassing matplotlib.scale.ScaleBase. We’ll create a scale that squashes the inner range and stretches the outer (qualified) ranges to make your measurements easier to inspect:

import matplotlib.pyplot as plt
import matplotlib.scale as mscale
import matplotlib.transforms as mtransforms
import pandas as pd
import numpy as np

class CustomRadialScale(mscale.ScaleBase):
    name = 'custom_radial'

    def __init__(self, axis, **kwargs):
        super().__init__(axis)
        # Critical thresholds from your requirements
        self.inner_threshold = 145
        self.upper_limit = 150
        # Adjust this to control inner range compression (smaller = more compression)
        self.compression_factor = 0.2

    def get_transform(self):
        return self.CustomRadialTransform(self.inner_threshold, self.upper_limit, self.compression_factor)

    def set_default_locators_and_formatters(self, axis):
        # Place ticks at key thresholds for clarity
        axis.set_major_locator(plt.FixedLocator([-150, -145, 0, 145, 150]))
        axis.set_major_formatter(plt.FormatStrFormatter('%d'))

    class CustomRadialTransform(mtransforms.Transform):
        input_dims = 1
        output_dims = 1
        is_separable = True

        def __init__(self, inner_threshold, upper_limit, compression_factor):
            super().__init__()
            self.inner_thresh = inner_threshold
            self.upper_limit = upper_limit
            self.compression = compression_factor
            self.stretch_range = upper_limit - inner_threshold

        def transform_non_affine(self, x):
            # Handle positive values: compress inner, stretch outer
            pos_mask = x >= 0
            pos_x = x[pos_mask]
            pos_transformed = np.where(
                pos_x <= self.inner_thresh,
                pos_x * self.compression,
                self.inner_thresh * self.compression + (pos_x - self.inner_thresh) * (1 - self.inner_thresh * self.compression) / self.stretch_range
            )
            # Handle negative values (mirror logic)
            neg_mask = x < 0
            neg_x = x[neg_mask]
            neg_transformed = np.where(
                neg_x >= -self.inner_thresh,
                neg_x * self.compression,
                -self.inner_thresh * self.compression + (neg_x + self.inner_thresh) * (1 - self.inner_thresh * self.compression) / self.stretch_range
            )
            # Combine results
            result = np.empty_like(x)
            result[pos_mask] = pos_transformed
            result[neg_mask] = neg_transformed
            return result

        def inverted(self):
            return CustomRadialScale.InvertedCustomRadialTransform(self.inner_thresh, self.upper_limit, self.compression)

    class InvertedCustomRadialTransform(mtransforms.Transform):
        input_dims = 1
        output_dims = 1
        is_separable = True

        def __init__(self, inner_threshold, upper_limit, compression_factor):
            super().__init__()
            self.inner_thresh = inner_threshold
            self.upper_limit = upper_limit
            self.compression = compression_factor
            self.stretch_range = upper_limit - inner_threshold

        def transform_non_affine(self, y):
            # Reverse the transformation for axis ticks
            pos_mask = y >= 0
            pos_y = y[pos_mask]
            pos_inverted = np.where(
                pos_y <= self.inner_thresh * self.compression,
                pos_y / self.compression,
                self.inner_thresh + (pos_y - self.inner_thresh * self.compression) * self.stretch_range / (1 - self.inner_thresh * self.compression)
            )
            neg_mask = y < 0
            neg_y = y[neg_mask]
            neg_inverted = np.where(
                neg_y >= -self.inner_thresh * self.compression,
                neg_y / self.compression,
                -self.inner_thresh + (neg_y + self.inner_thresh * self.compression) * self.stretch_range / (1 - self.inner_thresh * self.compression)
            )
            result = np.empty_like(y)
            result[pos_mask] = pos_inverted
            result[neg_mask] = neg_inverted
            return result

        def inverted(self):
            return CustomRadialScale.CustomRadialTransform(self.inner_thresh, self.upper_limit, self.compression)

# Register the custom scale with Matplotlib
mscale.register_scale(CustomRadialScale)

Step 2: Integrate with Your Data and Plot

Since you’re working with a circular plot, switching to polar coordinates will make the visualization more intuitive (matches your "CirclPlot" description). Here’s how to adapt your existing code:

# Read your CSV data
EBRData = pd.read_csv('C://Users/vanderey/Documents/MATLAB/EBRTest2.csv', header=0)

# Extract data columns
Rx = EBRData['xCoat']
Ry = EBRData['yCoat']
RLSLx = EBRData['xCoat_LSL']
RLSLy = EBRData['yCoat_LSL']
RUSLx = EBRData['xCoat_USL']
RUSLy = EBRData['yCoat_USL']

# Convert Cartesian coordinates to polar (r, theta) for natural circular plotting
def cart_to_polar(x, y):
    r = np.sqrt(x**2 + y**2)
    theta = np.arctan2(y, x)
    return r, theta

r_meas, theta_meas = cart_to_polar(Rx, Ry)
r_lsl, theta_lsl = cart_to_polar(RLSLx, RLSLy)
r_usl, theta_usl = cart_to_polar(RUSLx, RUSLy)

# Create the plot with custom scaling
my_dpi = 96
plt.figure(figsize=(480/my_dpi, 480/my_dpi), dpi=my_dpi)
ax = plt.subplot(projection='polar')

# Plot your measurements and limits
ax.plot(theta_meas, r_meas, color='blue', marker='.', linewidth=1, alpha=0.4, label='Measurements')
ax.plot(theta_lsl, r_lsl, color='red', marker='.', linewidth=1, alpha=0.4, label='LSL (146mm)')
ax.plot(theta_usl, r_usl, color='red', marker='.', linewidth=1, alpha=0.4, label='USL (150mm)')

# Apply the custom radial scale
ax.set_rscale('custom_radial')

# Adjust plot settings for clarity
ax.set_title('EBR - Custom Scaled Circular Plot', y=1.1)
ax.legend(loc='upper right', bbox_to_anchor=(1.3, 1.1))
plt.show()

Key Customization Tips

  • Tweak Compression: Modify the compression_factor in CustomRadialScale to control how much the inner range is squashed (smaller values = more zoom on the qualified range).
  • Cartesian Alternative: If you prefer to stick with your original Cartesian plot, replace the polar subplot setup with standard axes and apply the scale to both x and y:
    fig, ax = plt.subplots(figsize=(480/my_dpi, 480/my_dpi), dpi=my_dpi)
    ax.plot(Rx, Ry, color='blue', marker='.', linewidth=1, alpha=0.4)
    ax.set_xscale('custom_radial')
    ax.set_yscale('custom_radial')
    
  • Tick Adjustments: Update the FixedLocator values if you need to show additional tick marks for reference.

内容的提问来源于stack exchange,提问作者Jelle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:16:58