如何用Matplotlib平滑趋势线?附现有实现代码
Hey there! Let's tackle that jagged line problem you're facing with your plot. First, let's fix a small but important oversight in your current code: when you convert the W column to datetime, you're not assigning the result back to df['W']. That means your x-axis is still being treated as string values instead of proper datetime, which can mess up plotting logic. Let's correct that first, then dive into practical smoothing techniques.
Step 1: Fix the Datetime Conversion
First, update this line to ensure your datetime column is properly set:
df['W'] = pd.to_datetime(df['W']) # Assign the converted datetime back to df['W']
Now, let's look at three effective ways to smooth your trend lines, each with its own use case:
1. Rolling Average (Simple Moving Average)
This is a straightforward method for time-series data—it averages values over a sliding window to reduce noise. Perfect if you want a simple, interpretable smooth line that retains the general trend.
Here's how to implement it:
import pandas as pd import numpy as np import matplotlib.pyplot as plt # Generate and prep data df = pd.DataFrame(data=np.random.randn(5,4), index=['A','B','C','D','E'], columns=['W','X','Y','Z']) df['W'] = pd.to_datetime(['10/01/2018 12:00:00','10/03/2018 13:00:00', '10/03/2018 12:30:00','10/04/2018 12:05:00', '10/08/2018 12:00:15']) # Sort by datetime first (critical for rolling window logic!) df = df.sort_values('W') # Calculate rolling averages (window=2 means average 2 consecutive points) df['X_smoothed'] = df['X'].rolling(window=2, min_periods=1).mean() df['Y_smoothed'] = df['Y'].rolling(window=2, min_periods=1).mean() # Plot original and smoothed lines fig, ax = plt.subplots() # Original lines (lighter, transparent to highlight smoothed versions) df.plot(x="W", y="X", ax=ax, color='salmon', alpha=0.5, marker='o', label='Original X') df.plot(x="W", y="Y", ax=ax, color='royalblue', alpha=0.4, marker='o', label='Original Y') # Smoothed lines (bold, solid) df.plot(x="W", y="X_smoothed", ax=ax, color='salmon', linewidth=2, label='Smoothed X') df.plot(x="W", y="Y_smoothed", ax=ax, color='royalblue', linewidth=2, label='Smoothed Y') ax.legend() plt.xticks(rotation=45) plt.show()
Pro tip: Adjust the window parameter based on your data—larger windows mean more smoothing but less responsiveness to sudden changes.
2. LOESS/LOWESS Smoothing
LOESS (Locally Estimated Scatterplot Smoothing) fits a local regression to small subsets of your data, creating a flexible, non-linear smooth line. It's ideal for capturing complex trends without overfitting to noise.
You'll need the statsmodels library first—install it with pip install statsmodels:
import pandas as pd import numpy as np import matplotlib.pyplot as plt from statsmodels.nonparametric.smoothers_lowess import lowess # Generate and prep data df = pd.DataFrame(data=np.random.randn(5,4), index=['A','B','C','D','E'], columns=['W','X','Y','Z']) df['W'] = pd.to_datetime(['10/01/2018 12:00:00','10/03/2018 13:00:00', '10/03/2018 12:30:00','10/04/2018 12:05:00', '10/08/2018 12:00:15']) df = df.sort_values('W') # Convert datetime to numeric (LOESS works with numerical inputs) df['W_numeric'] = df['W'].astype('int64') / 10**9 # Convert to seconds since epoch # Apply LOESS smoothing (frac controls smoothing strength) x_smoothed = lowess(df['X'], df['W_numeric'], frac=0.5)[:, 1] y_smoothed = lowess(df['Y'], df['W_numeric'], frac=0.5)[:, 1] # Add smoothed values back to dataframe df['X_smoothed'] = x_smoothed df['Y_smoothed'] = y_smoothed # Plot fig, ax = plt.subplots() df.plot(x="W", y="X", ax=ax, color='salmon', alpha=0.5, marker='o', label='Original X') df.plot(x="W", y="Y", ax=ax, color='royalblue', alpha=0.4, marker='o', label='Original Y') df.plot(x="W", y="X_smoothed", ax=ax, color='salmon', linewidth=2, label='Smoothed X') df.plot(x="W", y="Y_smoothed", ax=ax, color='royalblue', linewidth=2, label='Smoothed Y') ax.legend() plt.xticks(rotation=45) plt.show()
Note: The frac parameter sets the smoothing strength—higher values (e.g., 0.7) mean more smoothing, lower values (e.g., 0.3) mean the line stays closer to your original data.
3. Spline Interpolation
Spline interpolation creates a smooth, continuous curve that passes through every one of your data points. It's great if you want a polished line that preserves the exact shape of your data while eliminating jagged edges.
Use scipy.interpolate for this—install scipy with pip install scipy:
import pandas as pd import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import make_interp_spline # Generate and prep data df = pd.DataFrame(data=np.random.randn(5,4), index=['A','B','C','D','E'], columns=['W','X','Y','Z']) df['W'] = pd.to_datetime(['10/01/2018 12:00:00','10/03/2018 13:00:00', '10/03/2018 12:30:00','10/04/2018 12:05:00', '10/08/2018 12:00:15']) df = df.sort_values('W') # Convert datetime to numeric df['W_numeric'] = df['W'].astype('int64') / 10**9 # Create a denser x-axis for a smoother curve w_dense = np.linspace(df['W_numeric'].min(), df['W_numeric'].max(), 100) # Generate spline curves spline_x = make_interp_spline(df['W_numeric'], df['X']) x_smoothed = spline_x(w_dense) spline_y = make_interp_spline(df['W_numeric'], df['Y']) y_smoothed = spline_y(w_dense) # Convert back to datetime for plotting w_dense_datetime = pd.to_datetime(w_dense, unit='s') # Plot fig, ax = plt.subplots() # Original data points ax.plot(df['W'], df['X'], color='salmon', alpha=0.5, marker='o', label='Original X') ax.plot(df['W'], df['Y'], color='royalblue', alpha=0.4, marker='o', label='Original Y') # Smoothed spline curves ax.plot(w_dense_datetime, x_smoothed, color='salmon', linewidth=2, label='Smoothed X') ax.plot(w_dense_datetime, y_smoothed, color='royalblue', linewidth=2, label='Smoothed Y') ax.legend() plt.xticks(rotation=45) plt.show()
Bonus: The 100 in np.linspace controls how dense the smooth curve is—higher numbers mean even smoother lines.
内容的提问来源于stack exchange,提问作者user9185511

