Matplotlib:在图像上正确叠加散点图的坐标与尺寸问题
Let’s work through your two problems and get your plot looking exactly how you want it:
1. Why Scatter Points Are Lined Up (Without Background Image)
The core issue here is that your coordinate values are strings, not numeric types. When you pass string values to plt.scatter(), Matplotlib treats them as categorical data instead of continuous numbers. That means it’s plotting each point at a discrete index on the x-axis (like 0,1,2...) instead of using your actual coordinate values—hence the straight line you’re seeing.
Fix: Convert Coordinates to Numeric Types
You need to turn those string tuples into floats. You can do this when unpacking the coordinates, or fix the DataFrame upfront:
# Option 1: Convert when unpacking plotdata x, y = zip(*plotdata) x = [float(val) for val in x] y = [float(val) for val in y] # Option 2: Fix the DataFrame first (cleaner if you reuse the data) import pandas as pd data = data.apply(pd.to_numeric) plotdata = list(data.to_records(index=False))
2. Aspect Ratio & Axis Distortion Issues (With Background Image)
The problem stems from Matplotlib’s default aspect='equal' setting for imshow(). This forces the image’s pixel aspect ratio to match the axis scale ratio, which doesn’t align with your coordinate ranges:
- Your X-axis spans 1500 units (-750 to 750)
- Your Y-axis spans 850 units (-400 to 450)
The defaultequalaspect squishes the axes to fit the image’s native pixel ratio, making your scatter points cluster in a tiny central area.
Fix: Adjust Image Aspect & Lock Axis Limits
Set aspect='auto' in ax.imshow() to let the image stretch to fit your axis ranges, and explicitly define your axis limits to ensure they match your desired range:
import matplotlib.pyplot as plt # Load image and create plot img = plt.imread("American-style pool table diagram.png") fig, ax = plt.subplots(figsize=(15, 9)) # Use aspect='auto' to fit image to your coordinate ranges ax.imshow(img, extent=[-750, 750, -400, 450], zorder=1, aspect='auto') # Lock axis limits to your specified range ax.set_xlim(-750, 750) ax.set_ylim(-400, 450) # Convert coordinates to floats and plot scatter points x, y = zip(*plotdata) x = [float(val) for val in x] y = [float(val) for val in y] ax.scatter(x, y, color='red', zorder=2, s=100) # s=100 makes points easier to spot # Add clear axis labels ax.set_xlabel("X Coordinate") ax.set_ylabel("Y Coordinate") plt.show()
Bonus: Clean Up Redundant Code
You had duplicate fig = plt.figure() and fig, ax = plt.subplots() calls—we’ve removed the redundant one to keep the code concise and avoid confusion.
Final Notes
- If you want to preserve your pool table image’s native aspect ratio (instead of stretching it), calculate the image’s pixel ratio (e.g., 1920x1080 = ~1.78) and set
ax.set_aspect(1.78)instead of usingaspect='auto'. Just note you may need to tweak your axis ranges to match this ratio if needed. - Always double-check that your coordinate data is numeric—string values are one of the most common plotting pitfalls!
内容的提问来源于stack exchange,提问作者ProximanovaMetropolis

