Skip to Content
ConceptsVisualizationTime Series Charts

Time Series Charts

Intermediate

Learning Objectives

After completing this recipe, you will be able to:

  • Create basic line charts
  • Compare multiple time series
  • Display moving averages and trends
  • Create interactive charts with Plotly

0. Setup

Load CSV files for data practice.

import pandas as pd import matplotlib.pyplot as plt # Load Data orders = pd.read_csv('src_orders.csv', parse_dates=['created_at']) items = pd.read_csv('src_order_items.csv') products = pd.read_csv('src_products.csv') # Merge for Analysis df = orders.merge(items, on='order_id').merge(products, on='product_id') # Ensure datetime conversion df['created_at'] = pd.to_datetime(df['created_at'], format='mixed')

1. Basic Line Chart

Matplotlib Basics

import pandas as pd import matplotlib.pyplot as plt # Daily sales data daily_sales = df.groupby(df['created_at'].dt.date)['sale_price'].sum() # Basic line chart plt.figure(figsize=(14, 6)) plt.plot(daily_sales.index, daily_sales.values, linewidth=2, color='steelblue') plt.title('Daily Sales Trend', fontsize=16, fontweight='bold') plt.xlabel('Date', fontsize=12) plt.ylabel('Sales ($)', fontsize=12) plt.grid(True, alpha=0.3) plt.xticks(rotation=45) plt.tight_layout() plt.show()

Basic Line Chart

2. Styling Options

You can improve readability by changing line styles, markers, colors, etc.

plt.figure(figsize=(14, 6)) plt.plot(daily_sales.index, daily_sales.values, color='green', linestyle='--', marker='o', markersize=8, label='Daily Sales') plt.title('Daily Sales Trend (Styled)', fontsize=16, fontweight='bold') plt.xlabel('Date', fontsize=12) plt.ylabel('Sales ($)', fontsize=12) plt.grid(True, alpha=0.3, linestyle='-.') plt.legend() plt.show()

Styled Line Chart

Styling Options

plt.figure(figsize=(14, 6)) plt.plot( daily_sales.index, daily_sales.values, linewidth=2, color='steelblue', marker='o', # Add marker markersize=4, markerfacecolor='white', markeredgewidth=1.5, label='Daily Sales' ) plt.fill_between( daily_sales.index, daily_sales.values, alpha=0.2, # Fill area color='steelblue' ) plt.title('Daily Sales Trend', fontsize=16, fontweight='bold') plt.xlabel('Date', fontsize=12) plt.ylabel('Sales ($)', fontsize=12) plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()
실행 결과
[Graph Saved: generated_plot_38d08c04e4_0.png]

Graph


2. Multiple Time Series Comparison

Overlapping Multiple Lines

# Monthly sales by category category_monthly = df.groupby([ df['created_at'].dt.to_period('M'), 'category' ])['sale_price'].sum().unstack() # Top 5 categories top_5 = df.groupby('category')['sale_price'].sum().nlargest(5).index category_monthly = category_monthly[top_5] # Multiple line chart plt.figure(figsize=(14, 6)) for col in category_monthly.columns: plt.plot( category_monthly.index.astype(str), category_monthly[col], marker='o', linewidth=2, markersize=5, label=col ) plt.title('Monthly Sales Trend by Category (Top 5)', fontsize=16, fontweight='bold') plt.xlabel('Month', fontsize=12) plt.ylabel('Sales ($)', fontsize=12) plt.legend(title='Category', bbox_to_anchor=(1.02, 1), loc='upper left') plt.xticks(rotation=45) plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()
실행 결과
[Graph Saved: generated_plot_7d30c61072_0.png]

Graph

Separating with Subplots

fig, axes = plt.subplots(2, 3, figsize=(16, 10), sharex=True) axes = axes.flatten() for i, col in enumerate(top_5): ax = axes[i] ax.plot(category_monthly.index.astype(str), category_monthly[col], marker='o', linewidth=2, color=f'C{i}') ax.set_title(col, fontsize=12, fontweight='bold') ax.grid(True, alpha=0.3) ax.tick_params(axis='x', rotation=45) # Hide empty subplots for j in range(len(top_5), len(axes)): axes[j].set_visible(False) fig.suptitle('Monthly Sales Trend by Category', fontsize=16, fontweight='bold') plt.tight_layout() plt.show()
실행 결과
[Graph Saved: generated_plot_ac6a666484_0.png]

Graph


Adding Moving Average

# Daily sales and moving averages daily_sales = df.groupby(df['created_at'].dt.date)['sale_price'].sum() ma_7 = daily_sales.rolling(window=7).mean() ma_30 = daily_sales.rolling(window=30).mean() plt.figure(figsize=(14, 6)) # Original data (light) plt.plot(daily_sales.index, daily_sales.values, alpha=0.3, color='gray', label='Daily Sales') # 7-day moving average plt.plot(daily_sales.index, ma_7.values, linewidth=2, color='steelblue', label='7-Day Moving Average') # 30-day moving average plt.plot(daily_sales.index, ma_30.values, linewidth=2, color='coral', label='30-Day Moving Average') plt.title('Daily Sales and Moving Averages', fontsize=16, fontweight='bold') plt.xlabel('Date', fontsize=12) plt.ylabel('Sales ($)', fontsize=12) plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()
실행 결과
[Graph Saved: generated_plot_e2af8308d9_0.png]

Graph

Trend Line (Linear Regression)

from scipy import stats import numpy as np # Linear regression x = np.arange(len(daily_sales)) slope, intercept, r_value, p_value, std_err = stats.linregress(x, daily_sales.values) trend_line = slope * x + intercept plt.figure(figsize=(14, 6)) plt.plot(daily_sales.index, daily_sales.values, alpha=0.5, color='steelblue', label='Daily Sales') plt.plot(daily_sales.index, trend_line, linewidth=2, color='red', linestyle='--', label=f'Trend (slope: {slope:.2f})') plt.title('Sales Trend Analysis', fontsize=16, fontweight='bold') plt.xlabel('Date', fontsize=12) plt.ylabel('Sales ($)', fontsize=12) plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() print(f"📈 Daily Average Growth: ${slope:.2f}") print(f"📊 R² = {r_value**2:.3f}")
실행 결과
[Graph Saved: generated_plot_5302302ced_0.png]
📈 Daily Average Growth: $5.25
📊 R² = 0.447

Graph


4. YoY/MoM Comparison Charts

Year-over-Year Same Month Comparison

# Monthly sales monthly = df.groupby([ df['created_at'].dt.year.rename('year'), df['created_at'].dt.month.rename('month') ])['sale_price'].sum().reset_index() # Pivot monthly_pivot = monthly.pivot(index='month', columns='year', values='sale_price') # Comparison chart plt.figure(figsize=(12, 6)) for year in monthly_pivot.columns: plt.plot( monthly_pivot.index, monthly_pivot[year], marker='o', linewidth=2, label=f'{year}' ) plt.title('Year-over-Year Monthly Sales Comparison', fontsize=16, fontweight='bold') plt.xlabel('Month', fontsize=12) plt.ylabel('Sales ($)', fontsize=12) plt.xticks(range(1, 13), ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']) plt.legend(title='Year') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()
실행 결과
[Graph Saved: generated_plot_a0e75c3b92_0.png]

Graph


5. Plotly Interactive Charts

Basic Line Chart

import plotly.express as px # Monthly sales monthly_sales = df.groupby(df['created_at'].dt.to_period('M'))['sale_price'].sum() monthly_df = pd.DataFrame({ 'month': monthly_sales.index.astype(str), 'sales': monthly_sales.values }) fig = px.line( monthly_df, x='month', y='sales', title='Monthly Sales Trend', markers=True ) fig.update_layout( xaxis_title='Month', yaxis_title='Sales ($)', hovermode='x unified' ) fig.show()

Multiple Time Series + Range Selector

import plotly.graph_objects as go fig = go.Figure() # Add multiple categories for cat in top_5: cat_data = category_monthly[cat] fig.add_trace(go.Scatter( x=cat_data.index.astype(str), y=cat_data.values, mode='lines+markers', name=cat, hovertemplate='%{y:$,.0f}<extra></extra>' )) fig.update_layout( title='Monthly Sales by Category (Interactive)', xaxis_title='Month', yaxis_title='Sales ($)', hovermode='x unified', legend_title='Category', # Range selector xaxis=dict( rangeselector=dict( buttons=list([ dict(count=3, label='3 Months', step='month', stepmode='backward'), dict(count=6, label='6 Months', step='month', stepmode='backward'), dict(step='all', label='All') ]) ), rangeslider=dict(visible=True) ) ) fig.show()

Quiz 1: Moving Average Analysis

Problem

For daily order count:

  1. Calculate 7-day and 14-day moving averages
  2. Display original data lightly, moving averages boldly
  3. Mark golden crosses (7-day MA > 14-day MA crossover points)

View Answer

# Daily order count daily_orders = df.groupby(df['created_at'].dt.date).size() ma_7 = daily_orders.rolling(7).mean() ma_14 = daily_orders.rolling(14).mean() # Find golden crosses golden_cross = (ma_7 > ma_14) & (ma_7.shift(1) <= ma_14.shift(1)) cross_dates = daily_orders[golden_cross].index plt.figure(figsize=(14, 6)) # Original (light) plt.plot(daily_orders.index, daily_orders.values, alpha=0.2, color='gray', label='Daily Orders') # Moving averages plt.plot(daily_orders.index, ma_7.values, linewidth=2, color='steelblue', label='7-Day MA') plt.plot(daily_orders.index, ma_14.values, linewidth=2, color='coral', label='14-Day MA') # Mark golden crosses for date in cross_dates: plt.axvline(date, color='gold', linestyle='--', alpha=0.7) plt.annotate('Golden Cross', xy=(date, daily_orders[date]), xytext=(10, 20), textcoords='offset points', fontsize=8, color='gold') plt.title('Daily Orders with Moving Averages (Golden Cross Marked)', fontsize=16, fontweight='bold') plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()
실행 결과
[Graph Saved: generated_plot_772a0ee4c3_0.png]

Graph


Summary

plt.plot() Key Parameters

ParameterDescriptionExample
linewidthLine thickness2
linestyleLine style'-', '--', ':'
markerMarker type'o', 's', '^'
colorColor'steelblue', '#1f77b4'
alphaTransparency0.5
labelLegend label'Sales'

Time Series Chart Selection Guide

SituationRecommended Chart
Single time seriesBasic line chart
Multiple comparisonOverlapping lines or subplots
Noise removalAdd moving average
Trend confirmationAdd regression line
InteractivePlotly

Next Steps

You’ve mastered time series charts! Next, learn about histograms, box plots, and other distribution analysis charts in Distribution Visualization.

Last updated on

🤖AI 모의면접실전처럼 연습하기