KR_Metplotlib - somaz94/python-study GitHub Wiki

Python Matplotlib ๊ฐœ๋… ์ •๋ฆฌ


1๏ธโƒฃ ๊ธฐ๋ณธ ๊ทธ๋ž˜ํ”„ ๊ทธ๋ฆฌ๊ธฐ

๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ ๊ทธ๋ž˜ํ”„ ์ž‘์„ฑ ๋ฐฉ๋ฒ•์ด๋‹ค.

import matplotlib.pyplot as plt
import numpy as np
from typing import Tuple, List, Optional, Union

def create_sine_plot(
    x_range: Tuple[float, float] = (0, 10),
    num_points: int = 100,
    color: str = 'b-',
    title: str = '๊ธฐ๋ณธ ์‚ฌ์ธ ๊ทธ๋ž˜ํ”„',
    figsize: Tuple[int, int] = (10, 6)
) -> None:
    """์‚ฌ์ธ ํ•จ์ˆ˜์˜ ๊ทธ๋ž˜ํ”„๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ‘œ์‹œํ•˜๋Š” ํ•จ์ˆ˜
    
    Args:
        x_range: x์ถ• ๋ฒ”์œ„ (์‹œ์ž‘, ๋)
        num_points: ๋ฐ์ดํ„ฐ ํฌ์ธํŠธ ์ˆ˜
        color: ์„  ์ƒ‰์ƒ ๋ฐ ์Šคํƒ€์ผ
        title: ๊ทธ๋ž˜ํ”„ ์ œ๋ชฉ
        figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ (๊ฐ€๋กœ, ์„ธ๋กœ) ์ธ์น˜ ๋‹จ์œ„
    """
    # ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    x = np.linspace(x_range[0], x_range[1], num_points)
    y = np.sin(x)

    # ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ
    plt.figure(figsize=figsize)
    plt.plot(x, y, color, label='sin(x)')
    plt.title(title)
    plt.xlabel('x ์ถ•')
    plt.ylabel('y ์ถ•')
    plt.legend()
    plt.grid(True)
    plt.show()

# ๊ธฐ๋ณธ ์‚ฌ์šฉ ์˜ˆ์‹œ
create_sine_plot()

# ์‚ฌ์šฉ์ž ์ •์˜ ์„ค์ • ์˜ˆ์‹œ
create_sine_plot(
    x_range=(-5, 5),
    num_points=200,
    color='r--',
    title='์‚ฌ์šฉ์ž ์ •์˜ ์‚ฌ์ธ ๊ทธ๋ž˜ํ”„',
    figsize=(12, 8)
)

# ์ง์ ‘ ๊ทธ๋ž˜ํ”„ ๊ทธ๋ฆฌ๊ธฐ
x = np.linspace(0, 10, 100)
y = np.sin(x)
y2 = np.cos(x)

plt.figure(figsize=(10, 6))
plt.plot(x, y, 'b-', label='sin(x)')
plt.plot(x, y2, 'r--', label='cos(x)')
plt.title('์‚ฌ์ธ๊ณผ ์ฝ”์‚ฌ์ธ ๊ทธ๋ž˜ํ”„')
plt.xlabel('x ์ถ•')
plt.ylabel('y ์ถ•')
plt.legend()
plt.grid(True)
plt.savefig('sine_cosine.png', dpi=300)  # ๊ณ ํ•ด์ƒ๋„๋กœ ์ €์žฅ
plt.show()

โœ… ํŠน์ง•:

  • ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ ์„ค์ •
  • ์ถ• ๋ ˆ์ด๋ธ” ์ง€์ •
  • ๋ฒ”๋ก€ ์ถ”๊ฐ€
  • ๊ฒฉ์ž์„  ํ‘œ์‹œ
  • ํŒŒ์ผ๋กœ ์ €์žฅ
  • ํ•จ์ˆ˜ํ™”๋œ ์ ‘๊ทผ๋ฒ•

๊ทธ๋ž˜ํ”„ ์š”์†Œ ์„ค๋ช…

Matplotlib์˜ ๊ธฐ๋ณธ ๊ทธ๋ž˜ํ”„ ์š”์†Œ๋“ค์ด๋‹ค.

  1. Figure: ์ „์ฒด ๊ทธ๋ž˜ํ”„ ์ปจํ…Œ์ด๋„ˆ
  2. Axes: ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ์˜์—ญ
  3. Axis: ์ถ• ์ •๋ณด์™€ ๋ˆˆ๊ธˆ์„ ๋‹ด๋‹น
  4. Line: ์„  ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„
  5. Marker: ๋ฐ์ดํ„ฐ ํฌ์ธํŠธ๋ฅผ ๊ฐ•์กฐํ•˜๋Š” ๊ธฐํ˜ธ
  6. Legend: ๊ทธ๋ž˜ํ”„ ์š”์†Œ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ฒ”๋ก€
  7. Title: ๊ทธ๋ž˜ํ”„ ์ œ๋ชฉ
  8. Grid: ๋ฐ์ดํ„ฐ ํŒ๋…์„ ๋•๋Š” ๊ฒฉ์ž์„ 


2๏ธโƒฃ ๋‹ค์–‘ํ•œ ๊ทธ๋ž˜ํ”„ ์œ ํ˜•

๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ๊ทธ๋ž˜ํ”„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์‹œ๊ฐํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

import matplotlib.pyplot as plt
import numpy as np
from typing import List, Tuple, Dict, Optional, Union, Any

class PlotGenerator:
    """๋‹ค์–‘ํ•œ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ์„ ์œ„ํ•œ ํด๋ž˜์Šค"""
    
    def __init__(self, style: str = 'default'):
        """์ดˆ๊ธฐํ™” ํ•จ์ˆ˜
        
        Args:
            style: ์ ์šฉํ•  matplotlib ์Šคํƒ€์ผ
        """
        if style != 'default':
            plt.style.use(style)
    
    def scatter_plot(
        self,
        x: np.ndarray,
        y: np.ndarray,
        figsize: Tuple[int, int] = (10, 6),
        color: Union[str, np.ndarray] = 'blue',
        alpha: float = 0.5,
        title: str = '์‚ฐ์ ๋„',
        save_path: Optional[str] = None
    ) -> None:
        """์‚ฐ์ ๋„ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ
        
        Args:
            x: x ์ขŒํ‘œ ๋ฐ์ดํ„ฐ
            y: y ์ขŒํ‘œ ๋ฐ์ดํ„ฐ
            figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ
            color: ์ ์˜ ์ƒ‰์ƒ
            alpha: ํˆฌ๋ช…๋„ (0~1)
            title: ๊ทธ๋ž˜ํ”„ ์ œ๋ชฉ
            save_path: ์ €์žฅํ•  ํŒŒ์ผ ๊ฒฝ๋กœ
        """
        plt.figure(figsize=figsize)
        plt.scatter(x, y, c=color, alpha=alpha)
        plt.title(title)
        plt.grid(True, linestyle='--', alpha=0.7)
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
        
        plt.show()
    
    def bar_plot(
        self,
        categories: List[str],
        values: List[float],
        figsize: Tuple[int, int] = (8, 6),
        color: Union[str, List[str]] = 'skyblue',
        title: str = '๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„',
        save_path: Optional[str] = None
    ) -> None:
        """๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ
        
        Args:
            categories: ๋ฒ”์ฃผ ๋ ˆ์ด๋ธ”
            values: ๊ฐ ๋ฒ”์ฃผ๋ณ„ ๊ฐ’
            figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ
            color: ๋ง‰๋Œ€ ์ƒ‰์ƒ
            title: ๊ทธ๋ž˜ํ”„ ์ œ๋ชฉ
            save_path: ์ €์žฅํ•  ํŒŒ์ผ ๊ฒฝ๋กœ
        """
        plt.figure(figsize=figsize)
        bars = plt.bar(categories, values, color=color)
        
        # ๋ง‰๋Œ€ ์œ„์— ๊ฐ’ ํ‘œ์‹œ
        for bar in bars:
            height = bar.get_height()
            plt.text(
                bar.get_x() + bar.get_width()/2.,
                height,
                f'{height:.1f}',
                ha='center', va='bottom'
            )
        
        plt.title(title)
        plt.grid(True, axis='y', linestyle='--', alpha=0.7)
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
        
        plt.show()
    
    def histogram(
        self,
        data: np.ndarray,
        bins: int = 30,
        figsize: Tuple[int, int] = (10, 6),
        color: str = 'skyblue',
        title: str = 'ํžˆ์Šคํ† ๊ทธ๋žจ',
        save_path: Optional[str] = None
    ) -> None:
        """ํžˆ์Šคํ† ๊ทธ๋žจ ์ƒ์„ฑ
        
        Args:
            data: ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด
            bins: ๊ตฌ๊ฐ„ ์ˆ˜
            figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ
            color: ํžˆ์Šคํ† ๊ทธ๋žจ ์ƒ‰์ƒ
            title: ๊ทธ๋ž˜ํ”„ ์ œ๋ชฉ
            save_path: ์ €์žฅํ•  ํŒŒ์ผ ๊ฒฝ๋กœ
        """
        plt.figure(figsize=figsize)
        plt.hist(data, bins=bins, color=color, edgecolor='black', alpha=0.7)
        plt.title(title)
        plt.grid(True, linestyle='--', alpha=0.7)
        plt.xlabel('๊ฐ’')
        plt.ylabel('๋นˆ๋„')
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
        
        plt.show()
    
    def pie_chart(
        self,
        sizes: List[float],
        labels: List[str],
        figsize: Tuple[int, int] = (8, 8),
        colors: Optional[List[str]] = None,
        explode: Optional[List[float]] = None,
        title: str = 'ํŒŒ์ด ์ฐจํŠธ',
        save_path: Optional[str] = None
    ) -> None:
        """ํŒŒ์ด ์ฐจํŠธ ์ƒ์„ฑ
        
        Args:
            sizes: ๊ฐ ์กฐ๊ฐ์˜ ํฌ๊ธฐ
            labels: ๊ฐ ์กฐ๊ฐ์˜ ๋ ˆ์ด๋ธ”
            figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ
            colors: ๊ฐ ์กฐ๊ฐ์˜ ์ƒ‰์ƒ
            explode: ํŠน์ • ์กฐ๊ฐ์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ’ (์˜ˆ: [0, 0.1, 0])
            title: ๊ทธ๋ž˜ํ”„ ์ œ๋ชฉ
            save_path: ์ €์žฅํ•  ํŒŒ์ผ ๊ฒฝ๋กœ
        """
        plt.figure(figsize=figsize)
        plt.pie(
            sizes, 
            labels=labels, 
            colors=colors,
            explode=explode,
            autopct='%1.1f%%',
            shadow=True,
            startangle=90
        )
        plt.axis('equal')  # ์›ํ˜• ์œ ์ง€
        plt.title(title)
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
        
        plt.show()

# ์‚ฌ์šฉ ์˜ˆ์‹œ
plot_gen = PlotGenerator(style='seaborn-v0_8-whitegrid')

# ์‚ฐ์ ๋„ ์˜ˆ์‹œ
x = np.random.randn(100)
y = np.random.randn(100)
plot_gen.scatter_plot(x, y, title='๋žœ๋ค ๋ถ„ํฌ ์‚ฐ์ ๋„')

# ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„ ์˜ˆ์‹œ
categories = ['A', 'B', 'C', 'D']
values = [4, 7, 2, 5]
plot_gen.bar_plot(categories, values, color=['red', 'green', 'blue', 'orange'])

# ํžˆ์Šคํ† ๊ทธ๋žจ ์˜ˆ์‹œ
data = np.random.normal(0, 1, 1000)
plot_gen.histogram(data, bins=30, title='์ •๊ทœ ๋ถ„ํฌ ํžˆ์Šคํ† ๊ทธ๋žจ')

# ํŒŒ์ด ์ฐจํŠธ ์˜ˆ์‹œ
sizes = [35, 30, 20, 15]
labels = ['A', 'B', 'C', 'D']
explode = [0, 0.1, 0, 0]  # B ์กฐ๊ฐ๋งŒ ๋ถ„๋ฆฌ
plot_gen.pie_chart(sizes, labels, explode=explode, title='์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„ํฌ')

โœ… ํŠน์ง•:

  • ๋‹ค์–‘ํ•œ ๊ทธ๋ž˜ํ”„ ์œ ํ˜•
  • ํˆฌ๋ช…๋„ ์กฐ์ ˆ
  • ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”
  • ๊ฐ์ฒด ์ง€ํ–ฅ์  ์ ‘๊ทผ๋ฒ•
  • ์‚ฌ์šฉ์ž ์ •์˜ ์˜ต์…˜
  • ์ €์žฅ ๊ธฐ๋Šฅ ํ†ตํ•ฉ


3๏ธโƒฃ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ

๊ทธ๋ž˜ํ”„์˜ ์‹œ๊ฐ์  ํ‘œํ˜„์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ ์„ค์ • ๋ฐฉ๋ฒ•์ด๋‹ค.

import matplotlib.pyplot as plt
import numpy as np
from typing import Optional, Tuple, List, Dict, Any
import matplotlib.cm as cm

def apply_style_demo(style_name: str = 'default') -> None:
    """๋‹ค์–‘ํ•œ ์Šคํƒ€์ผ ์ ์šฉ ๋ฐ๋ชจ
    
    Args:
        style_name: ์ ์šฉํ•  matplotlib ์Šคํƒ€์ผ
    """
    if style_name != 'default':
        plt.style.use(style_name)
    
    # ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    x = np.linspace(0, 10, 100)
    y1 = np.sin(x)
    y2 = np.cos(x)
    
    # ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ
    fig, ax = plt.subplots(figsize=(10, 6))
    ax.plot(x, y1, label='sin(x)')
    ax.plot(x, y2, label='cos(x)')
    ax.set_title(f'์Šคํƒ€์ผ: {style_name}')
    ax.set_xlabel('x ์ถ•')
    ax.set_ylabel('y ์ถ•')
    ax.legend()
    ax.grid(True)
    plt.show()

# ์ปฌ๋Ÿฌ๋งต ๋ฐ๋ชจ ํ•จ์ˆ˜
def colormap_demo(cmap_name: str = 'viridis') -> None:
    """๋‹ค์–‘ํ•œ ์ปฌ๋Ÿฌ๋งต ์‚ฌ์šฉ ๋ฐ๋ชจ
    
    Args:
        cmap_name: ์‚ฌ์šฉํ•  ์ปฌ๋Ÿฌ๋งต ์ด๋ฆ„
    """
    # ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    x = np.random.randn(1000)
    y = np.random.randn(1000)
    colors = np.random.rand(1000)
    
    fig, ax = plt.subplots(figsize=(10, 6))
    scatter = ax.scatter(x, y, c=colors, cmap=cmap_name, s=50, alpha=0.7)
    
    # ์ปฌ๋Ÿฌ๋ฐ” ์ถ”๊ฐ€
    cbar = plt.colorbar(scatter)
    cbar.set_label('๊ฐ’')
    
    ax.set_title(f'์ปฌ๋Ÿฌ๋งต: {cmap_name}')
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.grid(True, linestyle='--', alpha=0.7)
    plt.show()

# ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์Šคํƒ€์ผ ์ถœ๋ ฅ
available_styles = plt.style.available
print(f"์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์Šคํƒ€์ผ: {len(available_styles)}๊ฐœ")
print(available_styles[:5])  # ์ฒ˜์Œ 5๊ฐœ๋งŒ ์ถœ๋ ฅ

# ๋ช‡ ๊ฐ€์ง€ ์Šคํƒ€์ผ ๋ฐ๋ชจ
for style in ['default', 'seaborn-v0_8-darkgrid', 'ggplot', 'bmh']:
    apply_style_demo(style)

# ์ปฌ๋Ÿฌ๋งต ๋ฐ๋ชจ
for cmap in ['viridis', 'plasma', 'inferno', 'cividis', 'coolwarm']:
    colormap_demo(cmap)

# ์‚ฌ์šฉ์ž ์ •์˜ ์ƒ‰์ƒ ํŒ”๋ ˆํŠธ
x = np.linspace(0, 10, 100)
fig, ax = plt.subplots(figsize=(10, 6))

# ์‚ฌ์šฉ์ž ์ •์˜ ์ƒ‰์ƒ ์„ค์ •
colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
for i, alpha in enumerate([0.2, 0.4, 0.6, 0.8, 1.0]):
    ax.plot(x, np.sin(x + i*0.5), color=colors[i], 
            linewidth=2, alpha=alpha, 
            label=f'sin(x + {i*0.5:.1f})')

ax.set_title('์‚ฌ์šฉ์ž ์ •์˜ ์ƒ‰์ƒ ํŒ”๋ ˆํŠธ')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.legend()
ax.grid(True)
plt.show()

โœ… ํŠน์ง•:

  • ์Šคํƒ€์ผ ํ…Œ๋งˆ
  • ์ปฌ๋Ÿฌ๋งต ํ™œ์šฉ
  • ์‹œ๊ฐ์  ํšจ๊ณผ
  • ์ผ๊ด€๋œ ์‹œ๊ฐํ™”
  • ํˆฌ๋ช…๋„ ์กฐ์ •
  • ์‚ฌ์šฉ์ž ์ •์˜ ์ƒ‰์ƒ

์ธ๊ธฐ ์žˆ๋Š” ์Šคํƒ€์ผ๊ณผ ์ปฌ๋Ÿฌ๋งต

Matplotlib์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์Šคํƒ€์ผ๊ณผ ์ปฌ๋Ÿฌ๋งต์ด๋‹ค.

์ธ๊ธฐ ์žˆ๋Š” ์Šคํƒ€์ผ:

  1. seaborn-v0_8: ๊น”๋”ํ•˜๊ณ  ํ˜„๋Œ€์ ์ธ ํ†ต๊ณ„ ์‹œ๊ฐํ™” ์Šคํƒ€์ผ
  2. ggplot: R์˜ ggplot2์™€ ์œ ์‚ฌํ•œ ์Šคํƒ€์ผ
  3. bmh: Bayesian Methods for Hackers ์ฑ…์˜ ์Šคํƒ€์ผ
  4. fivethirtyeight: FiveThirtyEight ์›น์‚ฌ์ดํŠธ์™€ ์œ ์‚ฌํ•œ ์Šคํƒ€์ผ
  5. dark_background: ์–ด๋‘์šด ๋ฐฐ๊ฒฝ์˜ ์Šคํƒ€์ผ

์œ ์šฉํ•œ ์ปฌ๋Ÿฌ๋งต:

  1. viridis: ์—ฐ์†์ ์ธ ๋ฐ์ดํ„ฐ์— ์ตœ์ ํ™”๋œ ์ปฌ๋Ÿฌ๋งต (์ƒ‰์•ฝ ์นœํ™”์ )
  2. plasma: ๋†’์€ ๋Œ€๋น„์˜ ์ปฌ๋Ÿฌ๋งต
  3. coolwarm: ์–‘๊ทน ๋ฐ์ดํ„ฐ ํ‘œํ˜„์— ์ ํ•ฉํ•œ ์ปฌ๋Ÿฌ๋งต
  4. jet: ๊ณ ์ „์ ์ธ ๋ฌด์ง€๊ฐœ ์ปฌ๋Ÿฌ๋งต (ํŠน์ • ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉ ๊ถŒ์žฅ)
  5. Paired: ๋ฒ”์ฃผํ˜• ๋ฐ์ดํ„ฐ์— ์ ํ•ฉํ•œ ์ปฌ๋Ÿฌ๋งต


4๏ธโƒฃ ์„œ๋ธŒํ”Œ๋กฏ๊ณผ ๋ ˆ์ด์•„์›ƒ

์—ฌ๋Ÿฌ ๊ทธ๋ž˜ํ”„๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๋ฐฐ์น˜ํ•˜๋Š” ์„œ๋ธŒํ”Œ๋กฏ๊ณผ ๋ ˆ์ด์•„์›ƒ ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•์ด๋‹ค.

import matplotlib.pyplot as plt
import numpy as np
from typing import Tuple, List, Dict, Optional, Union
import matplotlib.gridspec as gridspec

class MultiplotManager:
    """์—ฌ๋Ÿฌ ๊ทธ๋ž˜ํ”„๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค"""
    
    def __init__(self, style: str = 'default'):
        """์ดˆ๊ธฐํ™” ํ•จ์ˆ˜
        
        Args:
            style: ์ ์šฉํ•  matplotlib ์Šคํƒ€์ผ
        """
        if style != 'default':
            plt.style.use(style)
    
    def create_basic_grid(
        self,
        rows: int,
        cols: int,
        figsize: Tuple[int, int] = (12, 10),
        title: Optional[str] = None
    ) -> Tuple[plt.Figure, np.ndarray]:
        """๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ์ƒ์„ฑ
        
        Args:
            rows: ํ–‰ ์ˆ˜
            cols: ์—ด ์ˆ˜
            figsize: ์ „์ฒด ๊ทธ๋ฆผ ํฌ๊ธฐ
            title: ์ „์ฒด ๊ทธ๋ฆผ ์ œ๋ชฉ
            
        Returns:
            ํŠœํ”Œ(Figure, Axes ๋ฐฐ์—ด)
        """
        fig, axes = plt.subplots(rows, cols, figsize=figsize)
        
        if title:
            fig.suptitle(title, fontsize=16)
        
        # ๋‹จ์ผ Axes์ธ ๊ฒฝ์šฐ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜
        if rows == 1 and cols == 1:
            axes = np.array([[axes]])
        elif rows == 1:
            axes = axes.reshape(1, cols)
        elif cols == 1:
            axes = axes.reshape(rows, 1)
        
        return fig, axes
    
    def create_complex_grid(
        self,
        layout: List[Dict[str, Union[int, Tuple[int, int, int, int]]]],
        figsize: Tuple[int, int] = (12, 10),
        title: Optional[str] = None
    ) -> Tuple[plt.Figure, Dict[str, plt.Axes]]:
        """๋ณต์žกํ•œ ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ์ƒ์„ฑ
        
        Args:
            layout: ๋ ˆ์ด์•„์›ƒ ์ •์˜ (๊ฐ ์š”์†Œ๋Š” name๊ณผ position์„ ํฌํ•จ)
            figsize: ์ „์ฒด ๊ทธ๋ฆผ ํฌ๊ธฐ
            title: ์ „์ฒด ๊ทธ๋ฆผ ์ œ๋ชฉ
            
        Returns:
            ํŠœํ”Œ(Figure, ์ด๋ฆ„-Axes ์‚ฌ์ „)
        """
        fig = plt.figure(figsize=figsize)
        if title:
            fig.suptitle(title, fontsize=16)
        
        gs = gridspec.GridSpec(12, 12)  # 12x12 ๊ทธ๋ฆฌ๋“œ ์‚ฌ์šฉ
        axes_dict = {}
        
        for item in layout:
            name = item['name']
            pos = item['position']  # (row_start, row_end, col_start, col_end)
            
            axes_dict[name] = fig.add_subplot(
                gs[pos[0]:pos[1], pos[2]:pos[3]]
            )
        
        return fig, axes_dict
    
    def example_basic_grid(self) -> None:
        """๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋“œ ์˜ˆ์‹œ"""
        # ๋ฐ์ดํ„ฐ ์ค€๋น„
        x = np.linspace(0, 10, 100)
        categories = ['A', 'B', 'C', 'D']
        values = [4, 7, 2, 5]
        random_data = np.random.randn(1000)
        
        # 2x2 ๊ทธ๋ฆฌ๋“œ ์ƒ์„ฑ
        fig, axes = self.create_basic_grid(2, 2, title="๊ธฐ๋ณธ ์„œ๋ธŒํ”Œ๋กฏ ๊ทธ๋ฆฌ๋“œ")
        
        # ์ฒซ ๋ฒˆ์งธ ๊ทธ๋ž˜ํ”„: ์„  ๊ทธ๋ž˜ํ”„
        axes[0, 0].plot(x, np.sin(x))
        axes[0, 0].set_title('์‚ฌ์ธ ํ•จ์ˆ˜')
        axes[0, 0].grid(True)
        
        # ๋‘ ๋ฒˆ์งธ ๊ทธ๋ž˜ํ”„: ์‚ฐ์ ๋„
        scatter_x = np.random.randn(30)
        scatter_y = np.random.randn(30)
        axes[0, 1].scatter(scatter_x, scatter_y)
        axes[0, 1].set_title('์‚ฐ์ ๋„')
        axes[0, 1].grid(True)
        
        # ์„ธ ๋ฒˆ์งธ ๊ทธ๋ž˜ํ”„: ๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„
        axes[1, 0].bar(categories, values)
        axes[1, 0].set_title('๋ง‰๋Œ€ ๊ทธ๋ž˜ํ”„')
        
        # ๋„ค ๋ฒˆ์งธ ๊ทธ๋ž˜ํ”„: ํžˆ์Šคํ† ๊ทธ๋žจ
        axes[1, 1].hist(random_data, bins=30)
        axes[1, 1].set_title('ํžˆ์Šคํ† ๊ทธ๋žจ')
        
        plt.tight_layout()
        plt.show()
    
    def example_complex_grid(self) -> None:
        """๋ณต์žกํ•œ ๊ทธ๋ฆฌ๋“œ ์˜ˆ์‹œ"""
        # ๋ ˆ์ด์•„์›ƒ ์ •์˜
        layout = [
            {'name': 'main', 'position': (0, 8, 0, 8)},  # ํฐ ๋ฉ”์ธ ํ”Œ๋กฏ
            {'name': 'right_top', 'position': (0, 4, 8, 12)},  # ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ
            {'name': 'right_bottom', 'position': (4, 8, 8, 12)},  # ์˜ค๋ฅธ์ชฝ ํ•˜๋‹จ
            {'name': 'bottom', 'position': (8, 12, 0, 12)}  # ํ•˜๋‹จ ์ „์ฒด
        ]
        
        fig, axes = self.create_complex_grid(layout, title="๋ณต์žกํ•œ ๋ ˆ์ด์•„์›ƒ ์˜ˆ์‹œ")
        
        # ๋ฐ์ดํ„ฐ ์ค€๋น„
        x = np.linspace(0, 10, 100)
        t = np.linspace(0, 1, 100)
        
        # ๋ฉ”์ธ ํ”Œ๋กฏ: 2D ์ปฌ๋Ÿฌ๋งต
        X, Y = np.meshgrid(x, x)
        Z = np.sin(X) * np.cos(Y)
        main = axes['main'].contourf(X, Y, Z, cmap='viridis')
        axes['main'].set_title('2D ์ปฌ๋Ÿฌ๋งต')
        plt.colorbar(main, ax=axes['main'])
        
        # ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ: ์„  ๊ทธ๋ž˜ํ”„
        axes['right_top'].plot(x, np.sin(x), 'r-')
        axes['right_top'].set_title('์‚ฌ์ธ ํ•จ์ˆ˜')
        
        # ์˜ค๋ฅธ์ชฝ ํ•˜๋‹จ: ์‚ฐ์ ๋„
        scatter_x = np.random.randn(50)
        scatter_y = np.random.randn(50)
        axes['right_bottom'].scatter(scatter_x, scatter_y)
        axes['right_bottom'].set_title('์‚ฐ์ ๋„')
        
        # ํ•˜๋‹จ: ์—ฌ๋Ÿฌ ์„  ๊ทธ๋ž˜ํ”„
        for i, freq in enumerate([1, 2, 3, 4]):
            axes['bottom'].plot(t, np.sin(2*np.pi*freq*t), 
                             label=f'f={freq} Hz')
        axes['bottom'].set_title('๋‹ค์–‘ํ•œ ์ฃผํŒŒ์ˆ˜')
        axes['bottom'].legend()
        
        plt.tight_layout()
        plt.show()

# ์‚ฌ์šฉ ์˜ˆ์‹œ
manager = MultiplotManager(style='seaborn-v0_8-whitegrid')
manager.example_basic_grid()
manager.example_complex_grid()

โœ… ํŠน์ง•:

  • ๋‹ค์ค‘ ๊ทธ๋ž˜ํ”„
  • ๋ ˆ์ด์•„์›ƒ ์กฐ์ •
  • ๊ทธ๋ž˜ํ”„ ๋ฐฐ์น˜
  • ๊ทธ๋ฆฌ๋“œ ์ŠคํŽ™ ํ™œ์šฉ
  • ๋ณต์žกํ•œ ๋ ˆ์ด์•„์›ƒ ์ƒ์„ฑ
  • ๊ฐ์ฒด ์ง€ํ–ฅ์  ์ ‘๊ทผ


5๏ธโƒฃ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ

Matplotlib์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๊ณผ 3D ์‹œ๊ฐํ™” ๋ฐฉ๋ฒ•์ด๋‹ค.

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
import matplotlib.patches as patches
from typing import Tuple, List, Dict, Optional, Callable, Any, Union

class AdvancedPlotting:
    """Matplotlib์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋Š” ํด๋ž˜์Šค"""
    
    def plot_3d_surface(
        self,
        x_range: Tuple[float, float] = (-5, 5),
        y_range: Tuple[float, float] = (-5, 5),
        grid_size: int = 100,
        func: Callable[[np.ndarray, np.ndarray], np.ndarray] = None,
        cmap: str = 'viridis',
        figsize: Tuple[int, int] = (10, 7),
        view_angle: Tuple[float, float] = (30, 45)
    ) -> None:
        """3D ํ‘œ๋ฉด ํ”Œ๋กฏ ์ƒ์„ฑ
        
        Args:
            x_range: x์ถ• ๋ฒ”์œ„
            y_range: y์ถ• ๋ฒ”์œ„
            grid_size: ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ
            func: ํ‘œ๋ฉด์„ ์ •์˜ํ•˜๋Š” ํ•จ์ˆ˜, ๊ธฐ๋ณธ๊ฐ’์€ sin(sqrt(x^2+y^2))
            cmap: ์ปฌ๋Ÿฌ๋งต
            figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ
            view_angle: ์‹œ์•ผ๊ฐ (elevation, azimuth)
        """
        if func is None:
            func = lambda x, y: np.sin(np.sqrt(x**2 + y**2))
        
        # ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
        x = np.linspace(x_range[0], x_range[1], grid_size)
        y = np.linspace(y_range[0], y_range[1], grid_size)
        X, Y = np.meshgrid(x, y)
        Z = func(X, Y)
        
        # 3D ํ”Œ๋กฏ ์ƒ์„ฑ
        fig = plt.figure(figsize=figsize)
        ax = fig.add_subplot(111, projection='3d')
        
        # ์„œํ”ผ์Šค ํ”Œ๋กฏ
        surface = ax.plot_surface(X, Y, Z, cmap=cmap, 
                                 edgecolor='none', alpha=0.8)
        
        # ๋“ฑ๊ณ ์„  ์ถ”๊ฐ€
        ax.contour(X, Y, Z, zdir='z', offset=Z.min(), cmap='coolwarm')
        
        # ์‹œ์•ผ๊ฐ ์„ค์ •
        ax.view_init(elev=view_angle[0], azim=view_angle[1])
        
        # ์ œ๋ชฉ๊ณผ ๋ ˆ์ด๋ธ”
        ax.set_title('3D ํ‘œ๋ฉด ํ”Œ๋กฏ', fontsize=14)
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')
        
        # ์ปฌ๋Ÿฌ๋ฐ” ์ถ”๊ฐ€
        fig.colorbar(surface, ax=ax, shrink=0.5, aspect=5)
        
        plt.show()
    
    def create_animation(
        self,
        frames: int = 100,
        interval: int = 50,
        figsize: Tuple[int, int] = (8, 6),
        save_path: Optional[str] = None
    ) -> None:
        """์• ๋‹ˆ๋ฉ”์ด์…˜ ์ƒ์„ฑ
        
        Args:
            frames: ํ”„๋ ˆ์ž„ ์ˆ˜
            interval: ํ”„๋ ˆ์ž„ ๊ฐ„ ๊ฐ„๊ฒฉ(๋ฐ€๋ฆฌ์ดˆ)
            figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ
            save_path: ์ €์žฅ ๊ฒฝ๋กœ
        """
        fig, ax = plt.subplots(figsize=figsize)
        ax.set_xlim(0, 2*np.pi)
        ax.set_ylim(-1.5, 1.5)
        
        line, = ax.plot([], [], lw=2)
        ax.grid(True)
        
        # ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜
        def init():
            line.set_data([], [])
            return line,
        
        # ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜
        def animate(i):
            x = np.linspace(0, 2*np.pi, 1000)
            phase = 2*np.pi * i / frames
            y = np.sin(x + phase)
            line.set_data(x, y)
            ax.set_title(f'์‚ฌ์ธ ํŒŒ๋™ (์œ„์ƒ: {phase:.2f} ๋ผ๋””์•ˆ)')
            return line,
        
        # ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ƒ์„ฑ
        anim = FuncAnimation(fig, animate, init_func=init,
                            frames=frames, interval=interval, blit=True)
        
        # ์ €์žฅ
        if save_path:
            anim.save(save_path, writer='pillow', fps=30)
        
        plt.show()
    
    def custom_plot_with_annotations(
        self,
        figsize: Tuple[int, int] = (10, 6)
    ) -> None:
        """์ฃผ์„๊ณผ ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ๊ฐ€ ์žˆ๋Š” ํ”Œ๋กฏ
        
        Args:
            figsize: ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ
        """
        fig, ax = plt.subplots(figsize=figsize)
        
        # ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
        x = np.linspace(0, 10, 100)
        y = np.sin(x)
        
        # ๊ธฐ๋ณธ ํ”Œ๋กฏ
        ax.plot(x, y, 'b-', lw=2)
        
        # ํŠน์ • ์ง€์  ๊ฐ•์กฐ
        max_point_idx = np.argmax(y)
        min_point_idx = np.argmin(y)
        
        # ํŠน์ • ์ง€์ ์— ๋งˆ์ปค ์ถ”๊ฐ€
        ax.plot(x[max_point_idx], y[max_point_idx], 'ro', ms=10)
        ax.plot(x[min_point_idx], y[min_point_idx], 'go', ms=10)
        
        # ํ…์ŠคํŠธ ์ฃผ์„ ์ถ”๊ฐ€
        ax.annotate('์ตœ๋Œ€๊ฐ’', xy=(x[max_point_idx], y[max_point_idx]),
                  xytext=(x[max_point_idx]+0.5, y[max_point_idx]+0.3),
                  arrowprops=dict(facecolor='red', shrink=0.05))
        
        ax.annotate('์ตœ์†Œ๊ฐ’', xy=(x[min_point_idx], y[min_point_idx]),
                  xytext=(x[min_point_idx]+0.5, y[min_point_idx]-0.3),
                  arrowprops=dict(facecolor='green', shrink=0.05))
        
        # ์˜์—ญ ๊ฐ•์กฐ
        ax.axvspan(2, 4, alpha=0.2, color='yellow')
        
        # ์‚ฌ์šฉ์ž ์ •์˜ ํŒจ์น˜ ์ถ”๊ฐ€
        rect = patches.Rectangle((7, 0.5), 2, 0.4, linewidth=1, 
                                edgecolor='r', facecolor='lightblue', alpha=0.5)
        ax.add_patch(rect)
        ax.text(8, 0.7, '๊ด€์‹ฌ ์˜์—ญ', ha='center')
        
        # ์ œ๋ชฉ๊ณผ ๋ ˆ์ด๋ธ”
        ax.set_title('์ฃผ์„์ด ์žˆ๋Š” ๊ทธ๋ž˜ํ”„', fontsize=14)
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.grid(True, linestyle='--', alpha=0.7)
        
        plt.tight_layout()
        plt.show()

# ์‚ฌ์šฉ ์˜ˆ์‹œ
advanced = AdvancedPlotting()

# 3D ํ‘œ๋ฉด ํ”Œ๋กฏ
advanced.plot_3d_surface()

# ๋‹ค๋ฅธ ํ•จ์ˆ˜๋กœ 3D ํ”Œ๋กฏ
advanced.plot_3d_surface(
    func=lambda x, y: np.cos(x) * np.sin(y),
    cmap='plasma',
    view_angle=(40, 30)
)

# ์• ๋‹ˆ๋ฉ”์ด์…˜
advanced.create_animation(save_path='wave_animation.gif')

# ์ฃผ์„์ด ์žˆ๋Š” ํ”Œ๋กฏ
advanced.custom_plot_with_annotations()

โœ… ํŠน์ง•:

  • 3D ์‹œ๊ฐํ™”
  • ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ํ‘œํ˜„
  • ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๊ธฐ๋Šฅ
  • ์• ๋‹ˆ๋ฉ”์ด์…˜
  • ์ฃผ์„ ๋ฐ ๊ฐ•์กฐ
  • ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ


์ฃผ์š” ํŒ

โœ… ๋ชจ๋ฒ” ์‚ฌ๋ก€:

  • ๊ทธ๋ž˜ํ”„ ํฌ๊ธฐ ์กฐ์ •: ๋ชฉ์ ์— ๋งž๋Š” ์ ์ ˆํ•œ ํฌ๊ธฐ์™€ ๋น„์œจ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€๋…์„ฑ์„ ๋†’์ธ๋‹ค.
  • ๋ ˆ์ด์•„์›ƒ ์ตœ์ ํ™”: ์—ฌ๋Ÿฌ ๊ทธ๋ž˜ํ”„๋ฅผ ํ‘œ์‹œํ•  ๋•Œ tight_layout()์ด๋‚˜ constrained_layout=True๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒน์นจ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.
  • ์Šคํƒ€์ผ ์ผ๊ด€์„ฑ ์œ ์ง€: ๋™์ผํ•œ ๋ณด๊ณ ์„œ๋‚˜ ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ์ผ๊ด€๋œ ์Šคํƒ€์ผ๊ณผ ์ปฌ๋Ÿฌ๋งต์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ปฌ๋Ÿฌ๋งต ์„ ํƒ: ๋ฐ์ดํ„ฐ ์œ ํ˜•์— ์ ํ•ฉํ•œ ์ปฌ๋Ÿฌ๋งต์„ ์„ ํƒํ•˜๊ณ , ์ƒ‰์•ฝ์ž๋ฅผ ์œ„ํ•ด viridis ๊ณ„์—ด์„ ๊ณ ๋ คํ•œ๋‹ค.
  • ๊ฐ€๋…์„ฑ ๊ณ ๋ ค: ์ ์ ˆํ•œ ๊ธ€๊ผด ํฌ๊ธฐ, ๋ฒ”๋ก€ ์œ„์น˜, ์ถ• ๋ ˆ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€๋…์„ฑ์„ ๋†’์ธ๋‹ค.
  • ์ €์žฅ ํฌ๋งท ์„ ํƒ: ๋ชฉ์ ์— ๋งž๋Š” ์ ์ ˆํ•œ ํŒŒ์ผ ํ˜•์‹(PNG, SVG, PDF)๊ณผ ํ•ด์ƒ๋„๋ฅผ ์„ ํƒํ•œ๋‹ค.
  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ: ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” ์‹œ plt.close()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.
  • ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๊ธฐ๋Šฅ ํ™œ์šฉ: ํ•„์š”์— ๋”ฐ๋ผ ipywidgets์ด๋‚˜ plotly์™€ ๊ฐ™์€ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋„๊ตฌ๋ฅผ ๊ณ ๋ คํ•œ๋‹ค.
  • ๋ ˆ์ด๋ธ”๋ง ๋ช…ํ™•ํ™”: ๋ชจ๋“  ์ถ•, ๋ฒ”๋ก€, ์ œ๋ชฉ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ ˆ์ด๋ธ”๋งํ•˜์—ฌ ํ•ด์„์„ ์šฉ์ดํ•˜๊ฒŒ ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๋“œ ํ™œ์šฉ: ๋ฐ์ดํ„ฐ ํŒ๋…์„ ๋•๊ธฐ ์œ„ํ•ด ์ ์ ˆํ•œ ๊ทธ๋ฆฌ๋“œ ์„ค์ •์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๊ฐ์ฒด ์ง€ํ–ฅ API ์‚ฌ์šฉ: ๋ณต์žกํ•œ ๊ทธ๋ž˜ํ”„๋Š” plt ํ•จ์ˆ˜๋ณด๋‹ค fig, ax ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•œ๋‹ค.
  • ํ•จ์ˆ˜ํ™”: ๋ฐ˜๋ณต์ ์ธ ์‹œ๊ฐํ™” ์ž‘์—…์€ ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๋กœ ๋ชจ๋“ˆํ™”ํ•˜์—ฌ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ธ๋‹ค.


โš ๏ธ **GitHub.com Fallback** โš ๏ธ