3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SVGAdvent Calendar 2024

Day 3

【Python × SVG】2×2マトリクス図解自動生成入門 〜戦略フレームワークを可視化する〜

Posted at

はじめに

ビジネス戦略の議論で頻繁に使用される2×2マトリクスを、Pythonを使って自動生成する方法をご紹介します。

image.png

必要なライブラリのインストール

pip install svgwrite

基本的な実装

import svgwrite

def create_2x2_matrix(filename, title, x_label, y_label, quadrant_labels):
    # SVGの作成
    dwg = svgwrite.Drawing(filename, size=('800px', '600px'))
    
    # 背景色の設定
    dwg.add(dwg.rect((0, 0), ('800px', '600px'), fill='white'))
    
    # マトリクスの基本設定
    margin = 50
    width = 700
    height = 500
    
    # マトリクスの外枠を描画
    dwg.add(dwg.rect((margin, margin), (width, height), 
                     fill='none', stroke='black', stroke_width=2))
    
    # 中央の十字線を描画
    mid_x = margin + width/2
    mid_y = margin + height/2
    dwg.add(dwg.line((mid_x, margin), (mid_x, margin + height), 
                     stroke='black', stroke_width=1))
    dwg.add(dwg.line((margin, mid_y), (margin + width, mid_y), 
                     stroke='black', stroke_width=1))
    
    # タイトルを追加
    dwg.add(dwg.text(title, 
                     insert=(400, 30),
                     text_anchor="middle",
                     style="font-size:20px; font-weight:bold"))
    
    # 軸ラベルを追加
    dwg.add(dwg.text(x_label,
                     insert=(400, 580),
                     text_anchor="middle",
                     style="font-size:14px"))
    
    # Y軸ラベル(回転)
    y_label_group = dwg.g(transform="rotate(-90 20 300)")
    y_label_group.add(dwg.text(y_label,
                              insert=(20, 300),
                              text_anchor="middle",
                              style="font-size:14px"))
    dwg.add(y_label_group)
    
    # 象限のラベルを追加
    for position, label in quadrant_labels.items():
        x = margin + (width * 0.25 if 'left' in position else width * 0.75)
        y = margin + (height * 0.25 if 'bottom' in position else height * 0.75)
        dwg.add(dwg.text(label,
                        insert=(x, y),
                        text_anchor="middle",
                        style="font-size:16px"))
    
    # SVGを保存
    dwg.save()

# BCGマトリクスの生成
def create_bcg_matrix():
    quadrant_labels = {
        'top_right': '花形\n(Star)',
        'top_left': '問題児\n(Question Mark)',
        'bottom_right': '金のなる木\n(Cash Cow)',
        'bottom_left': '負け犬\n(Dog)'
    }
    
    create_2x2_matrix(
        'bcg_matrix.svg',
        'BCGマトリクス',
        '相対的市場シェア →',
        '市場成長率 →',
        quadrant_labels
    )

# SWOT分析の生成
def create_swot_matrix():
    quadrant_labels = {
        'top_right': 'Strengths\n(強み)',
        'top_left': 'Opportunities\n(機会)',
        'bottom_right': 'Weaknesses\n(弱み)',
        'bottom_left': 'Threats\n(脅威)'
    }
    
    create_2x2_matrix(
        'swot_matrix.svg',
        'SWOT分析',
        '内部要因 ← → 外部要因',
        'プラス要因 ← → マイナス要因',
        quadrant_labels
    )

# リスク-リターン分析の生成
def create_risk_return_matrix():
    quadrant_labels = {
        'top_right': '理想的な投資',
        'top_left': '投機的な投資',
        'bottom_right': '安定的な投資',
        'bottom_left': '避けるべき投資'
    }
    
    create_2x2_matrix(
        'risk_return_matrix.svg',
        'リスク-リターン分析',
        'リスク →',
        'リターン →',
        quadrant_labels
    )

# 使用例
if __name__ == "__main__":
    # 各マトリクスを生成
    create_bcg_matrix()
    create_swot_matrix()
    create_risk_return_matrix()

実行結果の例

image.png

image.png

image.png

応用:カスタマイズ可能なバージョン

def create_custom_matrix(filename, config):
    """
    カスタマイズ可能な2×2マトリクス生成関数
    
    Parameters:
    -----------
    filename : str
        出力するSVGファイルの名前
    config : dict
        マトリクスの設定情報を含む辞書
        {
            'title': str,
            'x_label': str,
            'y_label': str,
            'quadrants': dict,
            'colors': dict (optional),
            'size': tuple (optional)
        }
    """
    # デフォルト値の設定
    default_config = {
        'size': ('800px', '600px'),
        'colors': {
            'background': 'white',
            'border': 'black',
            'text': 'black'
        }
    }
    
    # 設定をマージ
    config = {**default_config, **config}
    
    # SVGの作成
    dwg = svgwrite.Drawing(filename, size=config['size'])
    
    # 実装は基本バージョンと同様...(省略)
    
    return dwg

# 使用例
custom_config = {
    'title': 'カスタムマトリクス',
    'x_label': 'X軸 →',
    'y_label': 'Y軸 →',
    'quadrants': {
        'top_right': 'Quadrant 1',
        'top_left': 'Quadrant 2',
        'bottom_right': 'Quadrant 3',
        'bottom_left': 'Quadrant 4'
    },
    'colors': {
        'background': '#f0f0f0',
        'border': '#333333',
        'text': '#000000'
    },
    'size': ('1000px', '800px')
}

create_custom_matrix('custom_matrix.svg', custom_config)

実行結果の例

image.png

まとめ

このコードを使用することで、以下のようなことが可能です:

  1. 基本的な2×2マトリクスの自動生成
  2. タイトルや軸ラベルのカスタマイズ
  3. 象限内のテキスト配置
  4. サイズや色のカスタマイズ

発展的な使い方

  1. データの可視化との組み合わせ:
def add_data_points(dwg, points, margin, width, height):
    """
    マトリクス内にデータポイントを追加
    """
    for point in points:
        x = margin + (width * point['x'])
        y = margin + (height * (1 - point['y']))
        
        # ポイントを描画
        dwg.add(dwg.circle(center=(x, y), r=5, 
                          fill=point.get('color', 'red')))
        
        # ラベルを追加
        if 'label' in point:
            dwg.add(dwg.text(point['label'], 
                           insert=(x + 10, y), 
                           style="font-size:12px"))
  1. アニメーションの追加:
def add_animation(dwg):
    """
    SVGにアニメーション効果を追加
    """
    style = """
        @keyframes fade {
            from { opacity: 0; }
            to { opacity: 1; }
        }
        .animated {
            animation: fade 2s;
        }
    """
    dwg.defs.add(dwg.style(style))

注意点

  1. フォントの互換性:

    • SVG内で使用するフォントは、表示環境によって異なる場合があります
    • 重要な場合は、Web-safeフォントを使用するか、フォントを埋め込む必要があります
  2. ファイルサイズ:

    • 大量のデータポイントや複雑な図形を追加すると、ファイルサイズが大きくなる場合があります
    • 必要に応じて最適化を検討してください

例: カレーの評価や分類のための2×2マトリクス

image.png

import svgwrite

def create_curry_matrix(filename):
    """カレーを評価するための2×2マトリクスを生成(改善版)"""
    
    # SVGの基本設定
    dwg = svgwrite.Drawing(filename, size=('800px', '600px'))
    dwg.add(dwg.rect((0, 0), ('800px', '600px'), fill='#FFF8E7'))
    
    # マトリクスの基本設定
    margin = 50
    width = 700
    height = 500
    
    # メインタイトル
    dwg.add(dwg.text('カレー分析マトリクス',
                     insert=(400, 40),
                     text_anchor="middle",
                     font_size=24,
                     font_weight="bold",
                     fill='#8B4513'))
    
    # サブタイトル
    dwg.add(dwg.text('価格帯と味の特徴による分類',
                     insert=(400, 70),
                     text_anchor="middle",
                     font_size=16,
                     fill='#A0522D'))
    
    # マトリクスの外枠
    dwg.add(dwg.rect((margin, margin + 50), (width, height),
                     fill='none',
                     stroke='#8B4513',
                     stroke_width=2))
    
    # 中央の十字線
    mid_x = margin + width/2
    mid_y = margin + height/2 + 50
    dwg.add(dwg.line((mid_x, margin + 50), (mid_x, margin + height + 50),
                     stroke='#8B4513',
                     stroke_width=1))
    dwg.add(dwg.line((margin, mid_y), (margin + width, mid_y),
                     stroke='#8B4513',
                     stroke_width=1))
    
    # 象限の特徴を定義
    quadrants = {
        'top_right': {
            'title': '贅沢スパイシー',
            'items': [
                'ハイエンドインド料理',
                '高級スパイス使用',
                '専門店の本格カレー',
                '¥1,500〜'
            ]
        },
        'top_left': {
            'title': '大衆派スパイシー',
            'items': [
                '大衆インド料理店',
                '普通のスパイスカレー',
                'エスニック系チェーン店',
                '¥800〜1,500'
            ]
        },
        'bottom_right': {
            'title': '贅沢まろやか',
            'items': [
                '高級欧風カレー',
                'ホテルカレー',
                'オリジナルブレンド',
                '¥1,500〜'
            ]
        },
        'bottom_left': {
            'title': '大衆派まろやか',
            'items': [
                'カレーチェーン店',
                'ファミレスカレー',
                '家庭的カレー',
                '〜¥800'
            ]
        }
    }

    def add_curry_icon(x, y, size=40):
        """改良版カレーアイコン"""
        # アイコンの背景(円)
        dwg.add(dwg.circle((x, y), size/2,
                          fill='#FFFFFF',
                          stroke='#8B4513',
                          stroke_width=1,
                          opacity=0.7))
        # お皿(楕円)
        dwg.add(dwg.ellipse((x, y), (size*0.8, size/3),
                           fill='#FFF',
                           stroke='#8B4513'))
        # カレー(楕円)
        dwg.add(dwg.ellipse((x, y-size/6), (size*0.7, size/4),
                           fill='#DAA520'))
        
    # 軸ラベル(外側に配置)
    dwg.add(dwg.text('価格帯 →',
                     insert=(400, margin + height + 90),
                     text_anchor="middle",
                     font_size=16,
                     fill='#8B4513'))
    
    # Y軸ラベル(回転、外側に配置)
    y_label_group = dwg.g(transform=f"rotate(-90 25 {300})")
    y_label_group.add(dwg.text('味の特徴(スパイシー度) →',
                              insert=(25, 300),
                              text_anchor="middle",
                              font_size=16,
                              fill='#8B4513'))
    dwg.add(y_label_group)
    
    # 象限の描画(レイアウト改善)
    positions = {
        'top_right': (3/4, 1/4),
        'top_left': (1/4, 1/4),
        'bottom_right': (3/4, 3/4),
        'bottom_left': (1/4, 3/4)
    }
    
    for position, content in quadrants.items():
        x_ratio, y_ratio = positions[position]
        x = margin + width * x_ratio
        y = margin + height * y_ratio + 50
        
        # アイコンを上部に配置
        add_curry_icon(x, y - 60)
        
        # タイトルをアイコンの下に配置
        dwg.add(dwg.text(content['title'],
                        insert=(x, y - 20),
                        text_anchor="middle",
                        font_size=16,
                        font_weight="bold",
                        fill='#8B4513'))
        
        # 特徴リストをさらに下に配置
        for i, item in enumerate(content['items']):
            dwg.add(dwg.text(item,
                           insert=(x, y + 10 + i * 20),
                           text_anchor="middle",
                           font_size=12,
                           fill='#A0522D'))
    
    # 凡例(下部に整理して配置)
    legend_items = [
        ('価格帯', [
            '〜¥800: リーズナブル',
            '¥800〜1,500: スタンダード',
            '¥1,500〜: プレミアム'
        ]),
        ('スパイシー度', [
            'まろやか: 日本人向け味付け',
            'スパイシー: 本格的スパイス使用'
        ])
    ]
    
    legend_y = margin + height + 120
    for i, (title, items) in enumerate(legend_items):
        x = margin + i * 350
        
        # 凡例タイトルの背景
        dwg.add(dwg.rect((x-5, legend_y-15), (120, 20),
                        fill='#FFFFFF',
                        stroke='#8B4513',
                        stroke_width=1,
                        opacity=0.7))
        
        # 凡例タイトル
        dwg.add(dwg.text(title,
                        insert=(x, legend_y),
                        font_size=14,
                        font_weight="bold",
                        fill='#8B4513'))
        
        # 凡例アイテム
        for j, item in enumerate(items):
            dwg.add(dwg.text(item,
                           insert=(x, legend_y + 20 + j * 20),
                           font_size=12,
                           fill='#A0522D'))
    
    # SVGを保存
    dwg.save()

# マトリクスを生成
create_curry_matrix('curry_matrix_improved.svg')
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?