Matplotlib等高线图绘制求助:棒球好坏球数据可视化需求
Hey there! I get you—contour plots can be tricky when you start with scattered data instead of a pre-gridded dataset. Let's walk through exactly how to turn your baseball strike/ball data into a meaningful contour plot step by step.
First, let's clarify the core issue: plt.contour requires 2D grid arrays for x, y, and z, but your data is a list of scattered (x,y) points with binary z-values (0 for strikes, 1 for balls). So we need to convert your raw data into this grid format first. Depending on what you want your contour plot to show, there are two solid approaches:
Option 1: Contours of Ball (z=1) Density (Most Statistically Meaningful)
Since your z-values are categorical, showing the density of balls in different regions will give you a much more intuitive contour plot than trying to interpolate 0/1 values directly. We'll use kernel density estimation (KDE) for this:
import numpy as np import matplotlib.pyplot as plt from scipy.stats import kde # Replace these with your actual data arrays x = np.array([1.1, 0.9, 2.6, 3.1, 0.3]) y = np.array([2.1, 3.2, 4.1, 1.1, 0.9]) z = np.array([0, 1, 0, 1, 1]) # Isolate the coordinates for balls (z=1) ball_x = x[z == 1] ball_y = y[z == 1] # Create a regular grid to map our density onto xi, yi = np.meshgrid( np.linspace(x.min(), x.max(), 100), # 100 points along x-axis np.linspace(y.min(), y.max(), 100) # 100 points along y-axis ) # Calculate the KDE for ball positions kernel = kde.gaussian_kde([ball_x, ball_y]) zi = kernel(np.vstack([xi.flatten(), yi.flatten()])).reshape(xi.shape) # Plot the contours + original data plt.figure(figsize=(8, 6)) # Draw contour lines with labels contour_lines = plt.contour(xi, yi, zi, levels=5, cmap="Reds") plt.clabel(contour_lines, inline=True, fontsize=10) # Overlay original strike/ball points for context plt.scatter(x[z == 0], y[z == 0], c="blue", label="Strike (z=0)", alpha=0.6) plt.scatter(x[z == 1], y[z == 1], c="red", label="Ball (z=1)", alpha=0.6) plt.xlabel("X Coordinate") plt.ylabel("Y Coordinate") plt.title("Contour Plot of Ball Density") plt.legend() plt.show()
Option 2: Contour of Strike/Ball Boundary
If you specifically want to show the boundary between strike and ball regions, you can interpolate your binary z-values onto a grid and plot the 0.5 contour (the midpoint between 0 and 1):
import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import griddata # Same data setup as before x = np.array([1.1, 0.9, 2.6, 3.1, 0.3]) y = np.array([2.1, 3.2, 4.1, 1.1, 0.9]) z = np.array([0, 1, 0, 1, 1]) # Create the grid xi, yi = np.meshgrid( np.linspace(x.min(), x.max(), 100), np.linspace(y.min(), y.max(), 100) ) # Interpolate z-values onto the grid (use 'nearest' for sharp boundaries, 'linear' for smoother) zi = griddata((x, y), z, (xi, yi), method="nearest") # Plot the boundary contour + filled regions plt.figure(figsize=(8, 6)) # Draw the 0.5 boundary line plt.contour(xi, yi, zi, levels=[0.5], colors="black", linewidths=2) # Fill regions with color based on z-value plt.contourf(xi, yi, zi, cmap="coolwarm", alpha=0.3) # Overlay original points plt.scatter(x[z == 0], y[z == 0], c="blue", label="Strike (z=0)", alpha=0.6) plt.scatter(x[z == 1], y[z == 1], c="red", label="Ball (z=1)", alpha=0.6) plt.xlabel("X Coordinate") plt.ylabel("Y Coordinate") plt.title("Strike/Ball Boundary Contour Plot") plt.legend() plt.colorbar(label="Z Value (0=Strike, 1=Ball)") plt.show()
Key Notes:
- The
linspace(..., 100)parameter controls the resolution of your grid—higher numbers mean smoother contours but more computation. - For KDE, you can adjust the bandwidth of the kernel with
kernel.set_bandwidth(kernel.factor * 0.5)(smaller values = sharper density peaks) if needed. - If your dataset is very large, consider downsampling it first to speed up interpolation/KDE calculations.
内容的提问来源于stack exchange,提问作者JohnB

