Coverage for graphica/gui/plot_type_drawers.py: 97.8%
71 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
1"""
2組み込みの plot_type ごとの描き方。プラグインの register_plot_type と同じく「種類名 → 描画関数」の表で引く。
4描画関数は (canvas, ax, ds, x, y, plot_kwargs, axis_index) を受け取り、ds.artist にする artist を返す。
5x, y はウォーターフォールのずらし・欠損値の処理・表示用の間引きを済ませた点列。
6plot_kwargs(ウォーターフォールの zorder)はグラデーションの線には渡さない。
7"""
8import logging
10import numpy as np
11from scipy.stats import gaussian_kde
13from graphica.core.dataset import COLOR_BY_COLUMN_PLOT_TYPE, legend_text
14from graphica.core.marker_style import (
15 apply_mapped_hollow, line_marker_kwargs, mapped_scatter_marker_kwargs, scatter_marker_kwargs)
17logger = logging.getLogger(__name__)
20def _uses_line_gradient(ds):
21 return ds.gradient_enabled and ds.gradient_target in ('line', 'both')
24def _gradient_line(canvas, ax, ds, x, y):
25 return canvas._add_gradient_line(
26 ax, x, y, ds.color, ds.gradient_color2, ds.linewidth, ds.alpha, ds.linestyle, label=legend_text(ds))
29def _plain_line(ax, ds, x, y, plot_kwargs, **extra):
30 (artist,) = ax.plot(x, y, color=ds.color, linestyle=ds.linestyle, linewidth=ds.linewidth,
31 alpha=ds.alpha, label=legend_text(ds), **extra, **plot_kwargs)
32 return artist
35def _markers(ax, ds, x, y, plot_kwargs, **extra):
36 style = {'color': ds.color, **scatter_marker_kwargs(ds)}
37 return ax.scatter(x, y, marker=ds.marker, s=ds.markersize**2, alpha=ds.alpha, **style, **extra, **plot_kwargs)
40def _mapped_markers(ax, ds, x, y, plot_kwargs, **mapping):
41 """値で色分けするマーカー。mapping は c・cmap など色の対応。"""
42 style, hollow = mapped_scatter_marker_kwargs(ds)
43 artist = ax.scatter(x, y, marker=ds.marker, s=ds.markersize**2, alpha=ds.alpha, label=legend_text(ds),
44 **mapping, **style, **plot_kwargs)
45 if hollow:
46 apply_mapped_hollow(artist, ds)
47 return artist
50def draw_line(canvas, ax, ds, x, y, plot_kwargs, axis_index):
51 if _uses_line_gradient(ds):
52 return _gradient_line(canvas, ax, ds, x, y)
53 return _plain_line(ax, ds, x, y, plot_kwargs)
56def draw_scatter(canvas, ax, ds, x, y, plot_kwargs, axis_index):
57 return _markers(ax, ds, x, y, plot_kwargs, label=legend_text(ds))
60def draw_line_and_scatter(canvas, ax, ds, x, y, plot_kwargs, axis_index):
61 if _uses_line_gradient(ds):
62 # LineCollection はマーカーを描けないので、マーカーは単色で重ねる
63 artist = _gradient_line(canvas, ax, ds, x, y)
64 _markers(ax, ds, x, y, plot_kwargs)
65 return artist
66 (artist,) = ax.plot(x, y, color=ds.color, linestyle=ds.linestyle, linewidth=ds.linewidth,
67 marker=ds.marker, markersize=ds.markersize, alpha=ds.alpha, label=legend_text(ds),
68 **line_marker_kwargs(ds), **plot_kwargs)
69 return artist
72def draw_area(canvas, ax, ds, x, y, plot_kwargs, axis_index):
73 """0 との間を塗り、輪郭が分かるよう上端に線も重ねる。"""
74 if not ds.gradient_enabled:
75 artist = ax.fill_between(x, y, 0, color=ds.color, alpha=ds.alpha * 0.4, label=legend_text(ds), **plot_kwargs)
76 ax.plot(x, y, color=ds.color, linestyle=ds.linestyle, linewidth=ds.linewidth, alpha=ds.alpha, **plot_kwargs)
77 return artist
78 if ds.gradient_target in ('fill', 'both'):
79 artist = canvas._add_gradient_fill(ax, x, y, ds.color, ds.gradient_color2, ds.alpha * 0.4)
80 else:
81 artist = ax.fill_between(x, y, 0, color=ds.color, alpha=ds.alpha * 0.4, **plot_kwargs)
82 if ds.gradient_target in ('line', 'both'):
83 _gradient_line(canvas, ax, ds, x, y)
84 else:
85 _plain_line(ax, ds, x, y, plot_kwargs)
86 return artist
89def draw_bar(canvas, ax, ds, x, y, plot_kwargs, axis_index):
90 return ax.bar(x, y, color=ds.color, alpha=ds.alpha, label=legend_text(ds), **plot_kwargs)
93def draw_step(canvas, ax, ds, x, y, plot_kwargs, axis_index):
94 # 区間の左端の値を右端まで保つ(サンプリング・イベントデータの慣習)
95 return _plain_line(ax, ds, x, y, plot_kwargs, drawstyle='steps-post')
98def draw_density_scatter(canvas, ax, ds, x, y, plot_kwargs, axis_index):
99 """各点の位置での2次元カーネル密度を色にする。点が少ない・全点が同じ座標だと KDE が失敗するので単色にする。"""
100 try:
101 if len(x) < 3:
102 raise ValueError("点数不足")
103 xy = np.vstack([x, y])
104 density = gaussian_kde(xy)(xy)
105 return _mapped_markers(ax, ds, x, y, plot_kwargs, c=density, cmap=ds.colormap)
106 except (np.linalg.LinAlgError, ValueError):
107 return _markers(ax, ds, x, y, plot_kwargs, label=legend_text(ds))
110def draw_color_by_column(canvas, ax, ds, x, y, plot_kwargs, axis_index):
111 """
112 z_col_name の列の値で点を色分けする。z は visible_df 経由(ds.z_data)で取る
113 (df から取ると、行を除外したとたん点と色が1つずつずれる)。
114 """
115 z_values = ds.z_data
116 if z_values is not None and len(z_values) == len(x):
117 artist = _mapped_markers(ax, ds, x, y, plot_kwargs, c=z_values, cmap=ds.colormap, vmin=ds.vmin, vmax=ds.vmax)
118 # カラーバーは1軸に1つ。2Dマップが先に登録していればそちらを優先する
119 if axis_index not in canvas._axis_2d_mappables:
120 canvas._axis_2d_mappables[axis_index] = artist
121 return artist
122 if z_values is not None: 122 ↛ 124line 122 didn't jump to line 124 because the condition on line 122 was never true
123 # 欠損値の方針 'drop' で点が減ったときなど。黙って単色になると理由が分からないのでログに残す
124 logger.warning(
125 "'%s' のZ列の長さ(%d)がプロット点数(%d)と一致しないため、"
126 "単色のScatterとして描画します(欠損値の方針'drop'等で"
127 "配列が短くなっている可能性があります)。",
128 ds.name, len(z_values), len(x),
129 )
130 return _markers(ax, ds, x, y, plot_kwargs, label=legend_text(ds))
133BUILTIN_PLOT_TYPE_DRAWERS = {
134 'Line': draw_line,
135 'Scatter': draw_scatter,
136 'Line+Scatter': draw_line_and_scatter,
137 'Area': draw_area,
138 'Bar': draw_bar,
139 'Step': draw_step,
140 'Density Scatter': draw_density_scatter,
141 COLOR_BY_COLUMN_PLOT_TYPE: draw_color_by_column,
142}