Skip to content

Seaborn Lineplot: sns.lineplot() を使ったラインチャート完全ガイド

Updated on

ラインチャートは、トレンドや時系列データ、連続変数間の関係を可視化するために不可欠です。しかし、洞察を明確に伝える出版品質のラインプロットを作成するには、しばしば広範な matplotlib の設定と手動でのスタイリングが必要です。データサイエンティストは、分析に集中すべきときに、線の色の微調整、凡例の管理、軸のフォーマットに時間を浪費しています。

Seaborn の lineplot() 関数は、最小限のコードで美しいラインチャートを作成するための高レベルなインターフェースを提供することで、この問題を解決します。統計的な集計、信頼区間、カラーパレット、視覚的スタイリングを自動的に処理しながら、高度なユースケースのために高度にカスタマイズ可能です。

このガイドでは、基本的なラインプロットからカスタムスタイリングによる高度な多系列可視化まで、すべてをカバーします。データの洞察を効果的に伝えるプロフェッショナルなチャートの作成方法を学びます。

📚

sns.lineplot() の基本を理解する

sns.lineplot() 関数は、pandas DataFrame や配列からラインチャートを作成します。各 x 値における複数の観測値を自動的に集計し、デフォルトで信頼区間を表示します。

import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
 
# サンプルデータの作成
df = pd.DataFrame({
    'time': range(10),
    'value': [1, 3, 2, 5, 4, 6, 5, 7, 6, 8]
})
 
# 基本的な lineplot
sns.lineplot(data=df, x='time', y='value')
plt.title('Basic Line Plot')
plt.show()

これにより、列名から自動的に軸ラベルが付けられたクリーンなラインチャートが作成されます。この関数は、手動設定なしにデータのフォーマット、スケーリング、視覚的スタイリングを処理します。

主要なパラメータと構文

sns.lineplot() 関数は、複数の形式のデータを受け入れ、広範なカスタマイズオプションを提供します:

sns.lineplot(
    data=None,      # DataFrame、配列、または辞書
    x=None,         # x 軸の列名またはベクトル
    y=None,         # y 軸の列名またはベクトル
    hue=None,       # 色でのグループ化変数
    size=None,      # 線幅のグループ化変数
    style=None,     # 線スタイルのグループ化変数
    palette=None,   # カラーパレット
    markers=False,  # データポイントにマーカーを追加
    dashes=True,    # スタイルに応じて破線を使用
    ci=95,          # 信頼区間(新しいバージョンでは非推奨)
    errorbar=('ci', 95),  # 誤差の表現
    legend='auto',  # 凡例の表示
    ax=None         # Matplotlib の axes オブジェクト
)

DataFrame を使用する場合、x および y パラメータに列名を使用すると、より明確なコードになります:

# 複数観測値のデータセットを作成
data = pd.DataFrame({
    'day': [1, 2, 3, 1, 2, 3, 1, 2, 3],
    'sales': [100, 150, 120, 110, 145, 125, 105, 155, 130],
    'store': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C']
})
 
# 自動集計を使ってプロット
sns.lineplot(data=data, x='day', y='sales')
plt.title('Average Sales by Day (with 95% CI)')
plt.xlabel('Day of Week')
plt.ylabel('Sales ($)')
plt.show()

この関数は、日ごとの平均売上を自動的に計算し、信頼区間を線の周りの塗りつぶし帯として表示します。

Hue を使った複数の線のプロット

hue パラメータは、データ内の異なるグループに対して別々の線を作成し、自動的に異なる色を割り当てます:

# 同じプロット上に複数のストア
fig, ax = plt.subplots(figsize=(10, 6))
 
sns.lineplot(
    data=data,
    x='day',
    y='sales',
    hue='store',  # 各ストアに別々の線
    palette='Set2'
)
 
plt.title('Sales Comparison Across Stores')
plt.xlabel('Day of Week')
plt.ylabel('Sales ($)')
plt.legend(title='Store', loc='upper left')
plt.show()

これにより、自動的に凡例が生成された3つの異なる色の線が作成されます。palette パラメータはカラースキームを制御します。

より複雑なグループ化シナリオの場合:

# 複数のグループ化変数
customer_data = pd.DataFrame({
    'month': [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
    'revenue': [5000, 5500, 6000, 4800, 5200, 5800, 6200, 6800, 7200, 6000, 6500, 7000],
    'segment': ['Premium', 'Premium', 'Premium', 'Standard', 'Standard', 'Standard',
                'Premium', 'Premium', 'Premium', 'Standard', 'Standard', 'Standard'],
    'region': ['North', 'North', 'North', 'North', 'North', 'North',
               'South', 'South', 'South', 'South', 'South', 'South']
})
 
# segment を hue に、region を style に使用
sns.lineplot(
    data=customer_data,
    x='month',
    y='revenue',
    hue='segment',
    style='region',
    markers=True,
    dashes=False
)
 
plt.title('Revenue by Segment and Region')
plt.xlabel('Month')
plt.ylabel('Revenue ($)')
plt.show()

Style パラメータを使った線スタイルのカスタマイズ

style パラメータは、異なる破線パターンや線タイプを使用して線の外観を変化させます:

# 異なるセンサーの温度データ
temp_data = pd.DataFrame({
    'hour': list(range(24)) * 3,
    'temperature': [15, 14, 13, 12, 12, 13, 15, 17, 19, 21, 23, 24,
                   25, 26, 25, 24, 22, 20, 18, 17, 16, 15, 15, 14] * 3,
    'sensor': ['Sensor_A'] * 24 + ['Sensor_B'] * 24 + ['Sensor_C'] * 24,
    'location': ['Indoor'] * 24 + ['Outdoor'] * 24 + ['Basement'] * 24
})
 
# バリエーションを追加
import numpy as np
np.random.seed(42)
temp_data['temperature'] = temp_data['temperature'] + np.random.normal(0, 1, len(temp_data))
 
fig, ax = plt.subplots(figsize=(12, 6))
 
sns.lineplot(
    data=temp_data,
    x='hour',
    y='temperature',
    hue='location',
    style='location',
    markers=True,
    dashes=True,
    palette='tab10'
)
 
plt.title('24-Hour Temperature Monitoring')
plt.xlabel('Hour of Day')
plt.ylabel('Temperature (°C)')
plt.legend(title='Location')
plt.grid(True, alpha=0.3)
plt.show()

異なる線スタイルは、グレースケールで印刷する場合やアクセシビリティを考慮する場合に、グループを区別するのに役立ちます。

データポイントへのマーカーの追加

マーカーは、線に沿った個々のデータポイントを強調し、スパースなデータや特定の観測値を強調するのに役立ちます:

# 四半期の収益データ
earnings = pd.DataFrame({
    'quarter': ['Q1', 'Q2', 'Q3', 'Q4'] * 3,
    'earnings': [2.1, 2.3, 2.5, 2.4, 2.2, 2.4, 2.6, 2.7, 2.3, 2.5, 2.8, 2.9],
    'year': ['2023'] * 4 + ['2024'] * 4 + ['2025'] * 4
})
 
sns.lineplot(
    data=earnings,
    x='quarter',
    y='earnings',
    hue='year',
    markers=True,
    marker='o',  # 特定のマーカースタイル
    markersize=8,
    linewidth=2.5,
    palette='deep'
)
 
plt.title('Quarterly Earnings per Share')
plt.xlabel('Quarter')
plt.ylabel('EPS ($)')
plt.ylim(2.0, 3.0)
plt.legend(title='Year')
plt.grid(True, alpha=0.3)
plt.show()

グループごとにマーカースタイルを指定できます:

# 異なるグループに対するカスタムマーカー
sns.lineplot(
    data=earnings,
    x='quarter',
    y='earnings',
    hue='year',
    style='year',
    markers=['o', 's', '^'],  # 年ごとに異なるマーカー
    markersize=10,
    dashes=False
)
 
plt.title('Earnings Trend with Distinct Markers')
plt.show()

信頼区間とエラーバンドの扱い

Seaborn は、各 x 値に複数の観測値が存在する場合、自動的に信頼区間を計算します。この統計的な集計は、データの不確実性を伝えるのに役立ちます:

# 複製を含む実験データ
experiment = pd.DataFrame({
    'concentration': [0.1, 0.5, 1.0, 2.0, 5.0] * 10,
    'response': np.random.lognormal(mean=[1, 1.5, 2, 2.5, 3] * 10, sigma=0.3),
    'replicate': list(range(10)) * 5
})
 
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
 
# 95% 信頼区間(デフォルト)
sns.lineplot(data=experiment, x='concentration', y='response',
             errorbar=('ci', 95), ax=axes[0])
axes[0].set_title('95% Confidence Interval')
 
# 標準偏差
sns.lineplot(data=experiment, x='concentration', y='response',
             errorbar='sd', ax=axes[1])
axes[1].set_title('Standard Deviation')
 
# エラーバーなし
sns.lineplot(data=experiment, x='concentration', y='response',
             errorbar=None, ax=axes[2])
axes[2].set_title('No Error Bars')
 
plt.tight_layout()
plt.show()

errorbar パラメータは、複数の表現をサポートします:

  • ('ci', 95): 95% 信頼区間
  • ('pi', 95): 95% 予測区間
  • 'sd': 標準偏差
  • 'se': 標準誤差
  • ('pi', 50): 四分位範囲
  • None: 誤差の表現なし

パレットを使った色のカスタマイズ

palette パラメータは、名前付きパレットまたはカスタムカラーリストを使用して線の色を制御します:

# 株価比較
stocks = pd.DataFrame({
    'date': pd.date_range('2025-01-01', periods=60),
    'price': np.random.randn(60).cumsum() + 100,
    'ticker': ['AAPL'] * 60
})
 
stocks = pd.concat([
    stocks,
    pd.DataFrame({
        'date': pd.date_range('2025-01-01', periods=60),
        'price': np.random.randn(60).cumsum() + 150,
        'ticker': ['GOOGL'] * 60
    }),
    pd.DataFrame({
        'date': pd.date_range('2025-01-01', periods=60),
        'price': np.random.randn(60).cumsum() + 200,
        'ticker': ['MSFT'] * 60
    })
])
 
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
 
# 組み込みパレット
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
             palette='Set1', ax=axes[0, 0])
axes[0, 0].set_title('Set1 Palette')
 
# カスタムカラー
custom_colors = {'AAPL': '#FF6B6B', 'GOOGL': '#4ECDC4', 'MSFT': '#45B7D1'}
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
             palette=custom_colors, ax=axes[0, 1])
axes[0, 1].set_title('Custom Color Mapping')
 
# 色覚特性に配慮したパレット
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
             palette='colorblind', ax=axes[1, 0])
axes[1, 0].set_title('Colorblind Palette')
 
# ダークパレット
sns.lineplot(data=stocks, x='date', y='price', hue='ticker',
             palette='dark', ax=axes[1, 1])
axes[1, 1].set_title('Dark Palette')
 
plt.tight_layout()
plt.show()

人気のあるパレットオプションには以下が含まれます:

  • 'deep', 'muted', 'pastel', 'bright', 'dark', 'colorblind'
  • 'Set1', 'Set2', 'Set3', 'Paired', 'tab10'
  • 'viridis', 'plasma', 'inferno', 'magma', 'cividis'

時系列の可視化

Seaborn lineplot は、自動的な日付フォーマットで時系列の可視化に優れています:

# ウェブサイトトラフィックデータ
dates = pd.date_range('2025-01-01', periods=180, freq='D')
traffic = pd.DataFrame({
    'date': dates,
    'visits': 1000 + np.random.randn(180).cumsum() * 50 + np.sin(np.arange(180) / 7) * 200,
    'source': ['Organic'] * 180
})
 
# 有料トラフィックを追加
paid = pd.DataFrame({
    'date': dates,
    'visits': 500 + np.random.randn(180).cumsum() * 30,
    'source': ['Paid'] * 180
})
 
traffic = pd.concat([traffic, paid])
 
fig, ax = plt.subplots(figsize=(14, 6))
 
sns.lineplot(
    data=traffic,
    x='date',
    y='visits',
    hue='source',
    palette={'Organic': '#2ecc71', 'Paid': '#e74c3c'},
    linewidth=2
)
 
plt.title('Website Traffic Over Time', fontsize=16, fontweight='bold')
plt.xlabel('Date', fontsize=12)
plt.ylabel('Daily Visits', fontsize=12)
plt.legend(title='Traffic Source', title_fontsize=11, fontsize=10)
plt.grid(True, alpha=0.3, linestyle='--')
 
# x 軸の日付をフォーマット
import matplotlib.dates as mdates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1))
plt.xticks(rotation=45)
 
plt.tight_layout()
plt.show()

datetime インデックスを持つデータの場合:

# datetime インデックスを持つ時系列を作成
ts_data = pd.DataFrame({
    'value': np.random.randn(365).cumsum() + 50
}, index=pd.date_range('2025-01-01', periods=365))
 
# lineplot で使用するためにインデックスをリセット
ts_data_reset = ts_data.reset_index()
ts_data_reset.columns = ['date', 'value']
 
sns.lineplot(data=ts_data_reset, x='date', y='value')
plt.title('Time Series with Datetime Index')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

ワイド形式データからの複数の線のプロット

ワイド形式の DataFrame は、異なる変数を別々の列に格納します。Seaborn はこれらを直接プロットできます:

# ワイド形式データ
wide_data = pd.DataFrame({
    'month': range(1, 13),
    'Product_A': [100, 120, 115, 130, 140, 135, 150, 160, 155, 170, 180, 175],
    'Product_B': [80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135],
    'Product_C': [60, 65, 70, 68, 75, 80, 85, 90, 88, 95, 100, 105]
})
 
# 方法 1: ロング形式に変換
long_data = wide_data.melt(id_vars='month', var_name='Product', value_name='Sales')
 
sns.lineplot(data=long_data, x='month', y='Sales', hue='Product')
plt.title('Product Sales Comparison')
plt.xlabel('Month')
plt.ylabel('Sales')
plt.show()

または、ワイド形式から直接プロット:

# 方法 2: 各列を個別にプロット
fig, ax = plt.subplots(figsize=(10, 6))
 
for column in ['Product_A', 'Product_B', 'Product_C']:
    sns.lineplot(data=wide_data, x='month', y=column, label=column, ax=ax)
 
plt.title('Product Sales (Wide Format)')
plt.xlabel('Month')
plt.ylabel('Sales')
plt.legend(title='Product')
plt.show()

Matplotlib との組み合わせによる高度なカスタマイズ

Seaborn lineplot は、細かい制御のために matplotlib とシームレスに統合されます:

# カスタムスタイリングで図を作成
fig, ax = plt.subplots(figsize=(12, 7))
 
# 全体のスタイルを設定
sns.set_style('whitegrid')
sns.set_context('notebook', font_scale=1.2)
 
# データをプロット
performance = pd.DataFrame({
    'epoch': list(range(1, 51)) * 2,
    'accuracy': np.concatenate([
        0.6 + 0.008 * np.arange(50) + np.random.randn(50) * 0.02,
        0.55 + 0.009 * np.arange(50) + np.random.randn(50) * 0.025
    ]),
    'model': ['Model_A'] * 50 + ['Model_B'] * 50
})
 
sns.lineplot(
    data=performance,
    x='epoch',
    y='accuracy',
    hue='model',
    palette=['#FF6B6B', '#4ECDC4'],
    linewidth=2.5,
    ax=ax
)
 
# matplotlib でカスタマイズ
ax.set_title('Model Training Performance', fontsize=18, fontweight='bold', pad=20)
ax.set_xlabel('Training Epoch', fontsize=14, fontweight='bold')
ax.set_ylabel('Validation Accuracy', fontsize=14, fontweight='bold')
ax.set_ylim(0.5, 1.0)
ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: f'{y:.0%}'))
 
# 基準線を追加
ax.axhline(y=0.8, color='gray', linestyle='--', linewidth=1.5, alpha=0.7, label='Target (80%)')
 
# 凡例をカスタマイズ
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, title='Model', title_fontsize=12,
          fontsize=11, loc='lower right', framealpha=0.95)
 
# グリッドをカスタマイズ
ax.grid(True, alpha=0.4, linestyle=':', linewidth=0.8)
ax.set_facecolor('#F8F9FA')
 
# 注釈を追加
ax.annotate('Model A reaches 90%',
            xy=(45, 0.9), xytext=(35, 0.85),
            arrowprops=dict(arrowstyle='->', color='#FF6B6B', lw=1.5),
            fontsize=10, color='#FF6B6B', fontweight='bold')
 
plt.tight_layout()
plt.show()

複数のラインプロットを使ったサブプロットの作成

サブプロットレイアウトを使用してデータの異なる側面を比較します:

# 複数指標ダッシュボード
metrics = pd.DataFrame({
    'time': list(range(100)) * 3,
    'cpu_usage': np.random.rand(300) * 60 + 20 + np.sin(np.arange(300) / 10) * 15,
    'memory_usage': np.random.rand(300) * 40 + 40 + np.cos(np.arange(300) / 15) * 10,
    'disk_io': np.random.rand(300) * 80 + 10 + np.sin(np.arange(300) / 8) * 20,
    'server': ['Server_1'] * 100 + ['Server_2'] * 100 + ['Server_3'] * 100
})
 
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
 
# CPU Usage
sns.lineplot(data=metrics, x='time', y='cpu_usage', hue='server',
             palette='Set2', ax=axes[0], legend=True)
axes[0].set_title('CPU Usage (%)', fontsize=14, fontweight='bold')
axes[0].set_ylabel('Usage (%)')
axes[0].set_xlabel('')
axes[0].axhline(y=80, color='red', linestyle='--', alpha=0.5, label='Threshold')
axes[0].legend(loc='upper left', ncol=4)
 
# Memory Usage
sns.lineplot(data=metrics, x='time', y='memory_usage', hue='server',
             palette='Set2', ax=axes[1], legend=False)
axes[1].set_title('Memory Usage (%)', fontsize=14, fontweight='bold')
axes[1].set_ylabel('Usage (%)')
axes[1].set_xlabel('')
axes[1].axhline(y=80, color='red', linestyle='--', alpha=0.5)
 
# Disk I/O
sns.lineplot(data=metrics, x='time', y='disk_io', hue='server',
             palette='Set2', ax=axes[2], legend=False)
axes[2].set_title('Disk I/O (MB/s)', fontsize=14, fontweight='bold')
axes[2].set_ylabel('Throughput')
axes[2].set_xlabel('Time (seconds)', fontsize=12)
 
plt.tight_layout()
plt.show()

比較分析のためのグリッドレイアウト:

# 2x2 比較グリッド
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
 
categories = ['Category_A', 'Category_B', 'Category_C', 'Category_D']
positions = [(0, 0), (0, 1), (1, 0), (1, 1)]
 
for category, (i, j) in zip(categories, positions):
    subset = pd.DataFrame({
        'x': range(20),
        'y': np.random.randn(20).cumsum() + 10
    })
 
    sns.lineplot(data=subset, x='x', y='y', ax=axes[i, j],
                marker='o', linewidth=2, color='#3498db')
    axes[i, j].set_title(f'{category} Trend', fontweight='bold')
    axes[i, j].grid(True, alpha=0.3)
 
plt.suptitle('Multi-Category Performance Dashboard', fontsize=16, fontweight='bold', y=1.02)
plt.tight_layout()
plt.show()

比較表: sns.lineplot vs plt.plot vs sns.relplot

機能sns.lineplot()plt.plot()sns.relplot(kind="line")
自動集計あり(平均 + CI)なしあり(平均 + CI)
DataFrame 統合ネイティブサポート配列変換が必要ネイティブサポート
複数グループ(hue)自動配色手動の反復が必要自動配色
信頼区間組み込み手動計算が必要組み込み
FacetGrid サポートなし(単一 axes)なしあり(自動サブプロット)
統計的推定平均、中央値などなし平均、中央値など
セマンティックマッピングhue、size、style手動hue、size、style、col、row
デフォルトスタイリングSeaborn テーマMatplotlib デフォルトSeaborn テーマ
凡例の扱い自動手動自動
コードの複雑さ
パフォーマンス(大規模データ)高速
カスタマイズの深さ高(ax 経由)最高中(FacetGrid の制限あり)
最適なユースケース単一プロット、グループ化データシンプルなプロット、完全な制御多面比較

sns.lineplot() を使用する場合:自動集計と統計的可視化を単一のプロットで必要とするとき。

plt.plot() を使用する場合:最大限の制御、パフォーマンス、または事前に集計されたデータのプロットが必要なとき。

sns.relplot(kind="line") を使用する場合:自動サブプロット生成によるファセットプロットが必要なとき。

パラメータリファレンス表

パラメータデフォルト説明
dataDataFrame、dict、arrayNone入力データ構造
x, ystr、arrayNonex 軸および y 軸の変数
huestrNone色エンコーディングのグループ化変数
sizestrNone線幅のグループ化変数
stylestrNone線の破線パターンのグループ化変数
palettestr、list、dictNonehue レベルのカラーパレット
hue_orderlistNonehue 変数レベルの順序
unitsstrNoneサンプリング単位のグループ化(集計なし)
estimatorfunctionnp.mean集計関数(平均、中央値など)
errorbartuple、str('ci', 95)誤差表現方法
n_bootint1000CI のためのブートストラップ反復
seedintNoneブートストラップのための乱数シード
sortboolTrueプロット前に x 変数をソート
err_stylestr'band'誤差表示の 'band' または 'bars'
err_kwsdictNone誤差表現のキーワード引数
markersbool、listFalseデータポイントのマーカースタイル
dashesbool、listTrue線の破線パターン
legendstr、bool'auto'凡例の表示動作
ciint、'sd'、None非推奨代わりに errorbar を使用
axAxesNoneMatplotlib の axes オブジェクト
linewidthfloat1.5線の幅
linestylestr'-'線スタイル('-', '--', '-.', ':')

実世界の例: 株価分析

# 株価データをシミュレート
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=252, freq='B')  # 営業日
 
stocks_real = pd.DataFrame({
    'Date': np.tile(dates, 4),
    'Price': np.concatenate([
        100 * np.exp(np.random.randn(252).cumsum() * 0.01),  # AAPL
        150 * np.exp(np.random.randn(252).cumsum() * 0.012), # GOOGL
        200 * np.exp(np.random.randn(252).cumsum() * 0.011), # MSFT
        80 * np.exp(np.random.randn(252).cumsum() * 0.015)   # TSLA
    ]),
    'Ticker': ['AAPL'] * 252 + ['GOOGL'] * 252 + ['MSFT'] * 252 + ['TSLA'] * 252
})
 
# 正規化リターンを計算(ベース = 100)
stocks_normalized = stocks_real.copy()
for ticker in stocks_normalized['Ticker'].unique():
    mask = stocks_normalized['Ticker'] == ticker
    first_price = stocks_normalized.loc[mask, 'Price'].iloc[0]
    stocks_normalized.loc[mask, 'Normalized_Return'] = (
        stocks_normalized.loc[mask, 'Price'] / first_price * 100
    )
 
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10))
 
# 絶対価格
sns.lineplot(
    data=stocks_real,
    x='Date',
    y='Price',
    hue='Ticker',
    palette='deep',
    linewidth=2,
    ax=ax1
)
 
ax1.set_title('Stock Prices - Absolute Values (2024)', fontsize=16, fontweight='bold')
ax1.set_xlabel('')
ax1.set_ylabel('Price ($)', fontsize=12)
ax1.legend(title='Ticker', title_fontsize=11, fontsize=10, loc='upper left')
ax1.grid(True, alpha=0.3)
 
# 正規化リターン
sns.lineplot(
    data=stocks_normalized,
    x='Date',
    y='Normalized_Return',
    hue='Ticker',
    palette='deep',
    linewidth=2,
    ax=ax2
)
 
ax2.axhline(y=100, color='gray', linestyle='--', linewidth=1, alpha=0.7)
ax2.set_title('Normalized Returns (Base = 100)', fontsize=16, fontweight='bold')
ax2.set_xlabel('Date', fontsize=12)
ax2.set_ylabel('Normalized Return', fontsize=12)
ax2.legend(title='Ticker', title_fontsize=11, fontsize=10, loc='upper left')
ax2.grid(True, alpha=0.3)
 
plt.tight_layout()
plt.show()

実世界の例: センサーデータのモニタリング

# ノイズを含む IoT センサーデータ
hours = np.linspace(0, 24, 288)  # 5分間隔
sensor_data = pd.DataFrame({
    'time': np.tile(hours, 4),
    'temperature': np.concatenate([
        20 + 5 * np.sin(hours * np.pi / 12) + np.random.randn(288) * 0.5,  # Room 1
        22 + 4 * np.sin(hours * np.pi / 12 - 0.5) + np.random.randn(288) * 0.7,  # Room 2
        19 + 6 * np.sin(hours * np.pi / 12 + 0.3) + np.random.randn(288) * 0.6,  # Room 3
        21 + 5.5 * np.sin(hours * np.pi / 12 - 0.2) + np.random.randn(288) * 0.8   # Room 4
    ]),
    'room': ['Room_1'] * 288 + ['Room_2'] * 288 + ['Room_3'] * 288 + ['Room_4'] * 288,
    'building': ['Building_A'] * 576 + ['Building_B'] * 576
})
 
fig, ax = plt.subplots(figsize=(14, 7))
 
sns.lineplot(
    data=sensor_data,
    x='time',
    y='temperature',
    hue='room',
    style='building',
    palette='tab10',
    linewidth=2,
    markers=False,
    errorbar=('ci', 68),  # 1 標準偏差
    ax=ax
)
 
# 快適ゾーンを追加
ax.axhspan(18, 24, alpha=0.1, color='green', label='Comfort Zone')
 
ax.set_title('24-Hour Temperature Monitoring Across Rooms', fontsize=16, fontweight='bold')
ax.set_xlabel('Hour of Day', fontsize=12)
ax.set_ylabel('Temperature (°C)', fontsize=12)
ax.set_xticks(range(0, 25, 2))
ax.legend(title='Location', bbox_to_anchor=(1.05, 1), loc='upper left')
ax.grid(True, alpha=0.3, linestyle=':')
 
plt.tight_layout()
plt.show()

実世界の例: 時系列における A/B テスト結果

# 時系列での A/B テストのコンバージョン率
days = np.arange(1, 31)
ab_test = pd.DataFrame({
    'day': np.tile(days, 2),
    'conversion_rate': np.concatenate([
        0.05 + 0.001 * days + np.random.randn(30) * 0.005,  # Control
        0.055 + 0.0012 * days + np.random.randn(30) * 0.005  # Variant
    ]) * 100,
    'variant': ['Control'] * 30 + ['Variant_B'] * 30,
    'sample_size': np.random.randint(800, 1200, 60)
})
 
fig, axes = plt.subplots(2, 1, figsize=(12, 9), sharex=True)
 
# コンバージョン率のトレンド
sns.lineplot(
    data=ab_test,
    x='day',
    y='conversion_rate',
    hue='variant',
    palette={'Control': '#95a5a6', 'Variant_B': '#27ae60'},
    linewidth=2.5,
    markers=True,
    markersize=6,
    errorbar=None,
    ax=axes[0]
)
 
axes[0].set_title('A/B Test: Conversion Rate Over Time', fontsize=16, fontweight='bold')
axes[0].set_ylabel('Conversion Rate (%)', fontsize=12)
axes[0].set_xlabel('')
axes[0].legend(title='Test Group', fontsize=11)
axes[0].grid(True, alpha=0.3)
 
# サンプルサイズの追跡
sns.lineplot(
    data=ab_test,
    x='day',
    y='sample_size',
    hue='variant',
    palette={'Control': '#95a5a6', 'Variant_B': '#27ae60'},
    linewidth=2,
    markers=False,
    errorbar=None,
    ax=axes[1]
)
 
axes[1].set_title('Daily Sample Size', fontsize=14, fontweight='bold')
axes[1].set_ylabel('Users', fontsize=12)
axes[1].set_xlabel('Day of Test', fontsize=12)
axes[1].legend(title='Test Group', fontsize=11)
axes[1].grid(True, alpha=0.3)
 
plt.tight_layout()
plt.show()

ラインチャートの保存とエクスポート

レポートやプレゼンテーション用にさまざまな形式でプロットを保存します:

# エクスポート用の洗練されたチャートを作成
fig, ax = plt.subplots(figsize=(10, 6), dpi=100)
 
export_data = pd.DataFrame({
    'month': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'] * 2,
    'revenue': [50, 55, 53, 60, 65, 70, 45, 48, 50, 55, 58, 63],
    'region': ['North'] * 6 + ['South'] * 6
})
 
sns.lineplot(
    data=export_data,
    x='month',
    y='revenue',
    hue='region',
    palette='Set1',
    linewidth=3,
    markers=True,
    markersize=10,
    ax=ax
)
 
ax.set_title('Regional Revenue Comparison', fontsize=16, fontweight='bold', pad=15)
ax.set_xlabel('Month', fontsize=13)
ax.set_ylabel('Revenue ($K)', fontsize=13)
ax.legend(title='Region', title_fontsize=12, fontsize=11)
ax.grid(True, alpha=0.4)
 
# 複数の形式で保存
plt.savefig('revenue_comparison.png', dpi=300, bbox_inches='tight')  # 高解像度 PNG
plt.savefig('revenue_comparison.pdf', bbox_inches='tight')           # ベクター PDF
plt.savefig('revenue_comparison.svg', bbox_inches='tight')           # ウェブ用 SVG
 
# 透明な背景で保存
plt.savefig('revenue_comparison_transparent.png', dpi=300,
            bbox_inches='tight', transparent=True)
 
plt.show()
 
print("複数の形式でチャートを保存しました:")
print("- revenue_comparison.png (300 DPI)")
print("- revenue_comparison.pdf (ベクター)")
print("- revenue_comparison.svg (ウェブ)")
print("- revenue_comparison_transparent.png (透明)")

特定の寸法で保存:

# 出版用に正確な図のサイズを設定
fig = plt.figure(figsize=(8, 5))  # 幅、高さ(インチ)
ax = fig.add_subplot(111)
 
sns.lineplot(data=export_data, x='month', y='revenue', hue='region', ax=ax)
ax.set_title('Revenue Trends')
 
# 正確なピクセル寸法で保存(100 DPI の場合:8 インチ * 100 = 800 ピクセル)
plt.savefig('chart_800x500.png', dpi=100, bbox_inches='tight')
 
# 高解像度で保存(8 インチ * 300 DPI = 2400 ピクセル)
plt.savefig('chart_2400x1500.png', dpi=300, bbox_inches='tight')
 
plt.close()

PyGWalker を使ったインタラクティブなラインチャート

ラインチャートデータのインタラクティブな探索のために、PyGWalker を検討してください。これは、DataFrame を Tableau のようなインタラクティブな可視化に変換するオープンソースの Python ライブラリです:

# PyGWalker をインストール: pip install pygwalker
 
import pygwalker as pyg
import pandas as pd
import numpy as np
 
# 時系列データを作成
dates = pd.date_range('2025-01-01', periods=365, freq='D')
interactive_data = pd.DataFrame({
    'Date': dates,
    'Sales': 1000 + np.random.randn(365).cumsum() * 50,
    'Costs': 600 + np.random.randn(365).cumsum() * 30,
    'Region': np.random.choice(['North', 'South', 'East', 'West'], 365),
    'Product': np.random.choice(['Product_A', 'Product_B', 'Product_C'], 365)
})
 
# インタラクティブなエクスプローラーを起動
walker = pyg.walk(interactive_data)

PyGWalker は以下を提供します:

  • ドラッグアンドドロップインターフェースによるラインチャートの作成
  • インタラクティブなフィルタリングとグループ化
  • 自動集計と日付のビニング
  • 多系列比較
  • 静的チャートへのエクスポート
  • 各可視化のためにコードを書く必要なし

これは、異なるグループ化、時間範囲、集計を迅速にテストする必要がある探索的データ分析に特に役立ちます。インストールとドキュメントについては github.com/Kanaries/pygwalker (opens in a new tab) をご覧ください。

高度なヒントとベストプラクティス

大規模データセットの扱い

系列あたり数千ポイントを持つデータセットの場合、ダウンサンプリングや集計を検討してください:

# 大規模データセットのシミュレーション
large_data = pd.DataFrame({
    'timestamp': pd.date_range('2025-01-01', periods=10000, freq='T'),
    'value': np.random.randn(10000).cumsum()
})
 
# 方法 1: 1時間ごとにダウンサンプリング
hourly = large_data.set_index('timestamp').resample('H').mean().reset_index()
 
sns.lineplot(data=hourly, x='timestamp', y='value')
plt.title('Downsampled to Hourly Average')
plt.show()
 
# 方法 2: 自動集計のために estimator を使用
large_data['hour'] = large_data['timestamp'].dt.floor('H')
 
sns.lineplot(data=large_data, x='hour', y='value', estimator='median')
plt.title('Median Aggregation by Hour')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

複数の Estimator の組み合わせ

異なる統計的集計を比較します:

# ノイズの多い実験データ
experiment_multi = pd.DataFrame({
    'dose': [0.1, 0.5, 1.0, 2.0, 5.0] * 20,
    'response': np.random.lognormal([1, 1.5, 2, 2.5, 3] * 20, 0.4)
})
 
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
 
# CI を持つ平均
sns.lineplot(data=experiment_multi, x='dose', y='response',
             estimator='mean', errorbar=('ci', 95), ax=axes[0])
axes[0].set_title('Mean ± 95% CI')
 
# IQR を持つ中央値
sns.lineplot(data=experiment_multi, x='dose', y='response',
             estimator='median', errorbar=('pi', 50), ax=axes[1])
axes[1].set_title('Median ± IQR')
 
# カスタム estimator(75パーセンタイル)
sns.lineplot(data=experiment_multi, x='dose', y='response',
             estimator=lambda x: np.percentile(x, 75), errorbar=None, ax=axes[2])
axes[2].set_title('75th Percentile')
 
plt.tight_layout()
plt.show()

特定の領域の強調表示

重要な期間に注目を集めます:

# プロモーション期間の売上データ
sales_highlight = pd.DataFrame({
    'week': range(1, 53),
    'sales': 1000 + np.random.randn(52).cumsum() * 100 +
             np.where((np.arange(52) >= 20) & (np.arange(52) <= 30), 500, 0)
})
 
fig, ax = plt.subplots(figsize=(12, 6))
 
sns.lineplot(data=sales_highlight, x='week', y='sales', linewidth=2.5, color='#3498db')
 
# プロモーション期間を強調表示
ax.axvspan(20, 30, alpha=0.2, color='gold', label='Promotion Period')
ax.axhline(y=sales_highlight['sales'].mean(), color='red',
           linestyle='--', linewidth=1.5, alpha=0.7, label='Average Sales')
 
ax.set_title('Sales Performance with Promotion Period Highlighted',
             fontsize=16, fontweight='bold')
ax.set_xlabel('Week', fontsize=12)
ax.set_ylabel('Sales ($)', fontsize=12)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
 
plt.tight_layout()
plt.show()

よくある質問

結論

Seaborn の lineplot() 関数は、出版品質のラインチャートを作成するための強力でアクセスしやすいインターフェースを提供します。基本的なトレンド可視化から統計的集計を伴う複雑な多系列比較まで、最小限のコードで一般的なプロットタスクを処理しながら、matplotlib との統合を通じて完全なカスタマイズを保持します。

信頼区間の自動処理、セマンティックな色のマッピング、DataFrame 統合により、定型コードが排除され、プロットの仕組みではなくデータの洞察に集中できます。時系列トレンドの可視化、実験グループの比較、システムメトリクスのモニタリングのいずれであっても、sns.lineplot() は効果的にコミュニケーションするクリーンで有益な可視化を提供します。

静的な出版チャートの場合は、Seaborn の高レベルインターフェースを matplotlib の細かい制御と組み合わせます。インタラクティブな探索と迅速なプロトタイピングの場合は、PyGWalker などのツールが、各可視化のバリエーションに対してコードを書く必要なく、これらの機能をドラッグアンドドロップインターフェースに拡張します。

これらのテクニックを習得して、生のデータを、技術的および非技術的なオーディエンスの両方に洞察を伝え、意思決定を促す明確な視覚的ストーリーに変換してください。

📚