如何用Matplotlib分色填充两组非函数线性图的上下区域
Great question! The key challenge here is that your datasets aren't functional (x-values aren't monotonic or one-to-one), which makes plt.fill_between seem off-limits at first. But we can work around this by first converting your discrete points into continuous, monotonic curves using interpolation. Here's a step-by-step solution:
Step 1: Prepare and Sort Your Data
First, we need to sort both datasets by their x-values—this is required for interpolation tools to work correctly, as they expect monotonic input.
Step 2: Interpolate to Create Continuous Curves
We'll generate a dense set of x-values spanning the full range of both datasets, then interpolate y-values for each curve. This gives us the monotonic x array needed for plt.fill_between.
Full Code Implementation
import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import interp1d # Your original point sets a = [(1, 3), (3, 1), (7, 4), (6, 2), (13, 5)] b = [(0, 0), (3, 3), (6, 1), (10, 4), (8, 2), (12, 1)] # Convert to numpy arrays and sort by x-value a_arr = np.array(a) a_sorted = a_arr[a_arr[:, 0].argsort()] a_x, a_y = a_sorted[:, 0], a_sorted[:, 1] b_arr = np.array(b) b_sorted = b_arr[b_arr[:, 0].argsort()] b_x, b_y = b_sorted[:, 0], b_sorted[:, 1] # Define the full x range we need to cover x_min = min(a_x.min(), b_x.min()) x_max = max(a_x.max(), b_x.max()) # Generate dense x points for interpolation (1000 points ensures smoothness) x_interp = np.linspace(x_min, x_max, 1000) # Create interpolation functions (linear interpolation works well here; use 'cubic' for smoother curves if needed) interp_a = interp1d(a_x, a_y, kind='linear', fill_value="extrapolate") interp_b = interp1d(b_x, b_y, kind='linear', fill_value="extrapolate") # Get interpolated y values y_a_interp = interp_a(x_interp) y_b_interp = interp_b(x_interp) # Plot original points and interpolated curves plt.scatter(a_x, a_y, label='Set a', color='darkblue') plt.scatter(b_x, b_y, label='Set b', color='darkred') plt.plot(x_interp, y_a_interp, color='darkblue', alpha=0.7) plt.plot(x_interp, y_b_interp, color='darkred', alpha=0.7) # Fill between curves with different colors based on which is above plt.fill_between( x_interp, y_a_interp, y_b_interp, where=(y_a_interp > y_b_interp), color='limegreen', alpha=0.3, label='a above b' ) plt.fill_between( x_interp, y_a_interp, y_b_interp, where=(y_b_interp > y_a_interp), color='orange', alpha=0.3, label='b above a' ) # Add labels and legend plt.xlabel('X') plt.ylabel('Y') plt.title('Fill Between Non-Functional Curves') plt.legend() plt.show()
How This Works
- Sorting: By sorting each dataset's points by x-value, we ensure the interpolation function can correctly map x to y without ambiguity.
- Interpolation: Generating dense x points creates a monotonic x array that
plt.fill_betweenrequires. Thefill_value="extrapolate"ensures we cover the full range of both datasets, even where one set has no points. - Conditional Filling: The
whereparameter inplt.fill_betweenlets us target only the regions where one curve sits above the other, applying different colors accordingly.
Alternative (More Complex) Approach: Polygon Intersection
If you need to avoid interpolation and work strictly with the original discrete points, you can calculate the intersection points between the two polygonal lines, split the curves into segments at these intersections, then fill each segment based on which curve is on top. This requires more advanced geometry logic (e.g., using the shapely library to handle polygon intersections), but it's useful for cases where interpolation isn't acceptable.
内容的提问来源于stack exchange,提问作者yonzmeer

