bio-analyze-plot 0.1.0a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,35 @@
1
+ """
2
+ zh: 生物信息学绘图工具包。
3
+ en: Bioinformatics plotting toolkit.
4
+ """
5
+
6
+ from .plots import (
7
+ BarPlot,
8
+ BasePlot,
9
+ BoxPlot,
10
+ ChromosomePlot,
11
+ GSEAPlot,
12
+ HeatmapPlot,
13
+ LinePlot,
14
+ PCAPlot,
15
+ PiePlot,
16
+ ScatterPlot,
17
+ VolcanoPlot,
18
+ )
19
+ from .theme import PlotTheme, set_theme
20
+
21
+ __all__ = [
22
+ "BarPlot",
23
+ "BasePlot",
24
+ "BoxPlot",
25
+ "ChromosomePlot",
26
+ "GSEAPlot",
27
+ "HeatmapPlot",
28
+ "LinePlot",
29
+ "PCAPlot",
30
+ "PiePlot",
31
+ "ScatterPlot",
32
+ "VolcanoPlot",
33
+ "PlotTheme",
34
+ "set_theme",
35
+ ]
@@ -0,0 +1,578 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import pandas as pd
6
+ import typer
7
+
8
+ from bio_analyze_core.cli_i18n import detect_language, localize_app
9
+
10
+ from .plots.bar import BarPlot
11
+ from .plots.box import BoxPlot
12
+ from .plots.chromosome import ChromosomePlot
13
+ from .plots.gsea import GSEAPlot
14
+ from .plots.heatmap import HeatmapPlot
15
+ from .plots.line import LinePlot
16
+ from .plots.pca import PCAPlot
17
+ from .plots.pie import PiePlot
18
+ from .plots.scatter import ScatterPlot
19
+ from .plots.volcano import VolcanoPlot
20
+
21
+
22
+ def read_data(input_path: Path, sheet: str | None = None) -> pd.DataFrame:
23
+ """
24
+ zh: 从 CSV、TSV 或 Excel 文件读取数据。
25
+ en: Read data from CSV, TSV, or Excel file.
26
+
27
+ Args:
28
+ input_path (Path):
29
+ zh: 输入文件路径
30
+ en: Input file path
31
+ sheet (str | None, optional):
32
+ zh: Excel 工作表名称
33
+ en: Excel sheet name
34
+
35
+ Returns:
36
+ pd.DataFrame:
37
+ zh: 读取的数据
38
+ en: Loaded data
39
+ """
40
+ suffix = input_path.suffix.lower()
41
+ if suffix in [".csv"]:
42
+ return pd.read_csv(input_path)
43
+ elif suffix in [".tsv", ".txt"]:
44
+ return pd.read_csv(input_path, sep="\t")
45
+ elif suffix in [".xlsx", ".xls"]:
46
+ if sheet:
47
+ return pd.read_excel(input_path, sheet_name=sheet)
48
+ return pd.read_excel(input_path)
49
+ else:
50
+ # 默认为 csv
51
+ return pd.read_csv(input_path)
52
+
53
+
54
+ def get_app() -> typer.Typer:
55
+ app = typer.Typer(help="zh: 绘图工具\nen: Publication-ready plotting tools.")
56
+
57
+ @app.command("volcano")
58
+ def _volcano(
59
+ input_file: Path = typer.Argument(
60
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
61
+ ),
62
+ output: Path = typer.Option(
63
+ ...,
64
+ "-o",
65
+ "--output",
66
+ help="zh: 输出文件路径 (例如 volcano.png)。\nen: Output file path (e.g. volcano.png).",
67
+ ),
68
+ x: str = typer.Option(
69
+ "log2FoldChange", help="zh: log2 fold change 列名。\nen: Column name for log2 fold change."
70
+ ),
71
+ y: str = typer.Option("pvalue", help="zh: p-value 列名。\nen: Column name for p-value."),
72
+ theme: str = typer.Option(
73
+ "nature",
74
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
75
+ ),
76
+ fc_cutoff: float = typer.Option(1.0, help="zh: Fold change 截断值。\nen: Fold change cutoff."),
77
+ p_cutoff: float = typer.Option(0.05, help="zh: P-value 截断值。\nen: P-value cutoff."),
78
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
79
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
80
+ ) -> None:
81
+ """
82
+ zh: 从数据生成火山图。
83
+ en: Generate volcano plot from data.
84
+ """
85
+ df = read_data(input_file, sheet=sheet)
86
+ plotter = VolcanoPlot(theme=theme)
87
+ plotter.plot(
88
+ data=df,
89
+ x=x,
90
+ y=y,
91
+ fc_cutoff=fc_cutoff,
92
+ p_cutoff=p_cutoff,
93
+ title=title,
94
+ output=str(output),
95
+ )
96
+ typer.echo(f"Saved volcano plot to {output}")
97
+
98
+ @app.command("line")
99
+ def _line(
100
+ input_file: Path = typer.Argument(
101
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
102
+ ),
103
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
104
+ x: str = typer.Option(..., help="zh: X 轴列名。\nen: Column name for X-axis."),
105
+ y: str = typer.Option(..., help="zh: Y 轴列名。\nen: Column name for Y-axis."),
106
+ hue: str = typer.Option(None, help="zh: 分组列名 (hue)。\nen: Grouping column name (hue)."),
107
+ theme: str = typer.Option(
108
+ "nature",
109
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
110
+ ),
111
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
112
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
113
+ error_bar_type: str = typer.Option(None, help="zh: 误差棒类型: SD, SE, CI。\nen: Error bar type: SD, SE, CI."),
114
+ error_bar_ci: float = typer.Option(
115
+ 95, help="zh: 置信区间大小 (默认: 95)。\nen: Confidence interval size (default: 95)."
116
+ ),
117
+ error_bar_capsize: float = typer.Option(
118
+ 3.0, help="zh: 误差棒帽大小 (以点为单位)。\nen: Error bar capsize (in points)."
119
+ ),
120
+ markers: bool = typer.Option(
121
+ False, help="zh: 为数据点使用默认标记。\nen: Use default markers for data points."
122
+ ),
123
+ marker_style: str = typer.Option(
124
+ None,
125
+ help="zh: 指定标记符号 (逗号分隔, 例如 'o,s')。覆盖 --markers。\nen: Specific marker symbols (comma-separated, e.g. 'o,s'). Overrides --markers.",
126
+ ),
127
+ dashes: bool = typer.Option(True, help="zh: 为线条使用虚线样式。\nen: Use dashes for lines."),
128
+ smooth: bool = typer.Option(False, help="zh: 启用平滑曲线拟合。\nen: Enable smooth curve fitting."),
129
+ smooth_points: int = typer.Option(300, help="zh: 插值点数。\nen: Number of points for interpolation."),
130
+ ) -> None:
131
+ """
132
+ zh: 生成折线图。
133
+ en: Generate line plot.
134
+ """
135
+ df = read_data(input_file, sheet=sheet)
136
+
137
+ # Parse markers
138
+ markers_arg = markers
139
+ if marker_style:
140
+ if "," in marker_style:
141
+ markers_arg = [m.strip() for m in marker_style.split(",")]
142
+ else:
143
+ markers_arg = marker_style
144
+
145
+ plotter = LinePlot(theme=theme)
146
+ plotter.plot(
147
+ data=df,
148
+ x=x,
149
+ y=y,
150
+ hue=hue,
151
+ title=title,
152
+ output=str(output),
153
+ error_bar_type=error_bar_type,
154
+ error_bar_ci=error_bar_ci,
155
+ error_bar_capsize=error_bar_capsize,
156
+ markers=markers_arg,
157
+ dashes=dashes,
158
+ smooth=smooth,
159
+ smooth_points=smooth_points,
160
+ )
161
+ typer.echo(f"Saved line plot to {output}")
162
+
163
+ @app.command("bar")
164
+ def _bar(
165
+ input_file: Path = typer.Argument(
166
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
167
+ ),
168
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
169
+ x: str = typer.Option(..., help="zh: X 轴列名。\nen: Column name for X-axis."),
170
+ y: str = typer.Option(..., help="zh: Y 轴列名。\nen: Column name for Y-axis."),
171
+ hue: str = typer.Option(None, help="zh: 分组列名 (hue)。\nen: Grouping column name (hue)."),
172
+ theme: str = typer.Option(
173
+ "nature",
174
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
175
+ ),
176
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
177
+ error_bar_type: str = typer.Option(None, help="zh: 误差棒类型: SD, SE, CI。\nen: Error bar type: SD, SE, CI."),
178
+ error_bar_ci: float = typer.Option(
179
+ 95, help="zh: 置信区间大小 (默认: 95)。\nen: Confidence interval size (default: 95)."
180
+ ),
181
+ error_bar_max: str = typer.Option(
182
+ None, help="zh: 误差棒上限列名。\nen: Column name for error bar upper limit."
183
+ ),
184
+ error_bar_min: str = typer.Option(
185
+ None, help="zh: 误差棒下限列名。\nen: Column name for error bar lower limit."
186
+ ),
187
+ error_bar_capsize: float = typer.Option(0.1, help="zh: 误差棒帽大小。\nen: Error bar capsize."),
188
+ significance: list[str] = typer.Option(
189
+ None, help="zh: 显著性检验配对 (例如 'A,B' 'C,D')。\nen: Pairs for significance testing (e.g. 'A,B' 'C,D')."
190
+ ),
191
+ test: str = typer.Option("t-test_ind", help="zh: 统计检验方法。\nen: Statistical test method."),
192
+ text_format: str = typer.Option("star", help="zh: 显著性标注格式。\nen: Significance annotation format."),
193
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
194
+ ) -> None:
195
+ """
196
+ zh: 生成柱状图。
197
+ en: Generate bar plot.
198
+ """
199
+ df = read_data(input_file, sheet=sheet)
200
+
201
+ # 解析显著性对
202
+ sig_pairs = []
203
+ if significance:
204
+ for pair_str in significance:
205
+ parts = pair_str.split(",")
206
+ if len(parts) == 2:
207
+ sig_pairs.append((parts[0].strip(), parts[1].strip()))
208
+
209
+ plotter = BarPlot(theme=theme)
210
+ plotter.plot(
211
+ data=df,
212
+ x=x,
213
+ y=y,
214
+ hue=hue,
215
+ title=title,
216
+ output=str(output),
217
+ error_bar_type=error_bar_type,
218
+ error_bar_ci=error_bar_ci,
219
+ error_bar_max=error_bar_max,
220
+ error_bar_min=error_bar_min,
221
+ error_bar_capsize=error_bar_capsize,
222
+ significance=sig_pairs if sig_pairs else None,
223
+ test=test,
224
+ text_format=text_format,
225
+ )
226
+ typer.echo(f"Saved bar plot to {output}")
227
+
228
+ @app.command("box")
229
+ def _box(
230
+ input_file: Path = typer.Argument(
231
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
232
+ ),
233
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
234
+ x: str = typer.Option(..., help="zh: X 轴列名。\nen: Column name for X-axis."),
235
+ y: str = typer.Option(..., help="zh: Y 轴列名。\nen: Column name for Y-axis."),
236
+ hue: str = typer.Option(None, help="zh: 分组列名 (hue)。\nen: Grouping column name (hue)."),
237
+ theme: str = typer.Option(
238
+ "nature",
239
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
240
+ ),
241
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
242
+ significance: list[str] = typer.Option(
243
+ None, help="zh: 显著性检验配对 (例如 'A,B' 'C,D')。\nen: Pairs for significance testing (e.g. 'A,B' 'C,D')."
244
+ ),
245
+ test: str = typer.Option("t-test_ind", help="zh: 统计检验方法。\nen: Statistical test method."),
246
+ text_format: str = typer.Option("star", help="zh: 显著性标注格式。\nen: Significance annotation format."),
247
+ add_swarm: bool = typer.Option(False, help="zh: 叠加蜂群图点。\nen: Overlay swarmplot points."),
248
+ swarm_color: str = typer.Option(".25", help="zh: 蜂群图点颜色。\nen: Swarmplot point color."),
249
+ swarm_size: float = typer.Option(3.0, help="zh: 蜂群图点大小。\nen: Swarmplot point size."),
250
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
251
+ ) -> None:
252
+ """
253
+ zh: 生成箱线图。
254
+ en: Generate box plot.
255
+ """
256
+ df = read_data(input_file, sheet=sheet)
257
+
258
+ # 解析显著性对
259
+ sig_pairs = []
260
+ if significance:
261
+ for pair_str in significance:
262
+ parts = pair_str.split(",")
263
+ if len(parts) == 2:
264
+ sig_pairs.append((parts[0].strip(), parts[1].strip()))
265
+
266
+ plotter = BoxPlot(theme=theme)
267
+ plotter.plot(
268
+ data=df,
269
+ x=x,
270
+ y=y,
271
+ hue=hue,
272
+ title=title,
273
+ output=str(output),
274
+ significance=sig_pairs if sig_pairs else None,
275
+ test=test,
276
+ text_format=text_format,
277
+ add_swarm=add_swarm,
278
+ swarm_color=swarm_color,
279
+ swarm_size=swarm_size,
280
+ )
281
+ typer.echo(f"Saved box plot to {output}")
282
+
283
+ @app.command("heatmap")
284
+ def _heatmap(
285
+ input_file: Path = typer.Argument(
286
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
287
+ ),
288
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
289
+ index_col: str = typer.Option(None, help="zh: 用作行索引的列。\nen: Column to use as row index."),
290
+ cluster_rows: bool = typer.Option(True, help="zh: 是否聚类行。\nen: Whether to cluster rows."),
291
+ cluster_cols: bool = typer.Option(True, help="zh: 是否聚类列。\nen: Whether to cluster columns."),
292
+ z_score: int = typer.Option(
293
+ None,
294
+ help="zh: 标准化 (0: 行, 1: 列)。None 禁用。\nen: 0 (rows) or 1 (columns) for standardization. None to disable.",
295
+ ),
296
+ cmap: str = typer.Option(
297
+ None, help="zh: 颜色映射名称 (例如 'vlag', 'coolwarm')。\nen: Colormap name (e.g. 'vlag', 'coolwarm')."
298
+ ),
299
+ center: float = typer.Option(None, help="zh: 颜色映射居中的值。\nen: Value at which to center the colormap."),
300
+ theme: str = typer.Option(
301
+ "nature",
302
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
303
+ ),
304
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
305
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
306
+ ) -> None:
307
+ """
308
+ zh: 生成热图/聚类图。
309
+ en: Generate heatmap/clustermap.
310
+ """
311
+ df = read_data(input_file, sheet=sheet)
312
+
313
+ # 准备传递给 plot 的 kwargs,过滤掉 None 的值以允许使用默认值/主题值
314
+ kwargs = {}
315
+ if cmap is not None:
316
+ kwargs["cmap"] = cmap
317
+ if center is not None:
318
+ kwargs["center"] = center
319
+
320
+ plotter = HeatmapPlot(theme=theme)
321
+ plotter.plot(
322
+ data=df,
323
+ index_col=index_col,
324
+ cluster_rows=cluster_rows,
325
+ cluster_cols=cluster_cols,
326
+ z_score=z_score,
327
+ title=title,
328
+ output=str(output),
329
+ **kwargs,
330
+ )
331
+ typer.echo(f"Saved heatmap to {output}")
332
+
333
+ @app.command("pca")
334
+ def _pca(
335
+ input_file: Path = typer.Argument(
336
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
337
+ ),
338
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
339
+ hue: str = typer.Option(None, help="zh: 分组列名 (hue)。\nen: Grouping column name (hue)."),
340
+ style: str = typer.Option(None, help="zh: 标记样式列名。\nen: Column name for marker style."),
341
+ size: str = typer.Option(None, help="zh: 标记大小列名。\nen: Column name for marker size."),
342
+ index_col: str = typer.Option(
343
+ None, help="zh: 用作索引的列 (例如基因名)。\nen: Column to use as index (e.g. gene names)."
344
+ ),
345
+ transpose: bool = typer.Option(
346
+ True,
347
+ help="zh: 转置输入数据 (默认: True, 假设 Genes x Samples)。\nen: Transpose input data (default: True, assumes Genes x Samples).",
348
+ ),
349
+ theme: str = typer.Option(
350
+ "nature",
351
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
352
+ ),
353
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
354
+ cluster: bool = typer.Option(False, help="zh: 执行 KMeans 聚类。\nen: Perform KMeans clustering."),
355
+ n_clusters: int = typer.Option(3, help="zh: KMeans 聚类的簇数。\nen: Number of clusters for KMeans."),
356
+ add_ellipse: bool = typer.Option(
357
+ False, help="zh: 为每个分组绘制置信椭圆。\nen: Draw confidence ellipses for each group."
358
+ ),
359
+ ellipse_std: float = typer.Option(
360
+ 2.0, help="zh: 置信椭圆的标准差。\nen: Standard deviation for confidence ellipses."
361
+ ),
362
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
363
+ ) -> None:
364
+ """
365
+ zh: 生成 PCA 图。
366
+ en: Generate PCA plot.
367
+ """
368
+ df = read_data(input_file, sheet=sheet)
369
+ plotter = PCAPlot(theme=theme)
370
+ plotter.plot(
371
+ data=df,
372
+ hue=hue,
373
+ style=style,
374
+ size=size,
375
+ index_col=index_col,
376
+ transpose=transpose,
377
+ title=title,
378
+ cluster=cluster,
379
+ n_clusters=n_clusters,
380
+ add_ellipse=add_ellipse,
381
+ ellipse_std=ellipse_std,
382
+ output=str(output),
383
+ )
384
+ typer.echo(f"Saved PCA plot to {output}")
385
+
386
+ @app.command("scatter")
387
+ def _scatter(
388
+ input_file: Path = typer.Argument(
389
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
390
+ ),
391
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
392
+ x: str = typer.Option(..., help="zh: X 轴列名。\nen: Column name for X-axis."),
393
+ y: str = typer.Option(..., help="zh: Y 轴列名。\nen: Column name for Y-axis."),
394
+ hue: str = typer.Option(None, help="zh: 分组列名 (hue)。\nen: Grouping column name (hue)."),
395
+ style: str = typer.Option(None, help="zh: 标记样式列名。\nen: Column name for marker style."),
396
+ size: str = typer.Option(None, help="zh: 标记大小列名。\nen: Column name for marker size."),
397
+ theme: str = typer.Option(
398
+ "nature",
399
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
400
+ ),
401
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
402
+ add_ellipse: bool = typer.Option(
403
+ False, help="zh: 为每个分组绘制置信椭圆。\nen: Draw confidence ellipses for each group."
404
+ ),
405
+ ellipse_std: float = typer.Option(
406
+ 2.0, help="zh: 置信椭圆的标准差。\nen: Standard deviation for confidence ellipses."
407
+ ),
408
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
409
+ ) -> None:
410
+ """
411
+ zh: 生成散点图。
412
+ en: Generate scatter plot.
413
+ """
414
+ df = read_data(input_file, sheet=sheet)
415
+ plotter = ScatterPlot(theme=theme)
416
+ plotter.plot(
417
+ data=df,
418
+ x=x,
419
+ y=y,
420
+ hue=hue,
421
+ style=style,
422
+ size=size,
423
+ title=title,
424
+ output=str(output),
425
+ add_ellipse=add_ellipse,
426
+ ellipse_std=ellipse_std,
427
+ )
428
+ typer.echo(f"Saved scatter plot to {output}")
429
+
430
+ @app.command("pie")
431
+ def _pie(
432
+ input_file: Path = typer.Argument(
433
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
434
+ ),
435
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
436
+ x: str = typer.Option(..., help="zh: 标签列名 (分类变量)。\nen: Column name for labels (categorical)."),
437
+ y: str = typer.Option(..., help="zh: 数值列名。\nen: Column name for values (numerical)."),
438
+ theme: str = typer.Option(
439
+ "nature",
440
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
441
+ ),
442
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
443
+ autopct: str = typer.Option("%1.1f%%", help="zh: 百分比格式。\nen: Percentage format."),
444
+ startangle: float = typer.Option(90, help="zh: 起始角度。\nen: Start angle."),
445
+ explode: str = typer.Option(
446
+ None,
447
+ help="zh: 扇区偏移。可以是列名或逗号分隔的数值。\nen: Explode slices. Can be a column name or comma-separated values.",
448
+ ),
449
+ shadow: bool = typer.Option(False, help="zh: 显示阴影。\nen: Show shadow."),
450
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
451
+ ) -> None:
452
+ """
453
+ zh: 生成饼图。
454
+ en: Generate pie chart.
455
+ """
456
+ df = read_data(input_file, sheet=sheet)
457
+
458
+ explode_arg = None
459
+ if explode:
460
+ if explode in df.columns:
461
+ explode_arg = explode
462
+ else:
463
+ try:
464
+ explode_arg = [float(v.strip()) for v in explode.split(",")]
465
+ except ValueError:
466
+ explode_arg = None
467
+ typer.echo(f"Warning: Invalid explode format '{explode}'. Ignored.")
468
+
469
+ plotter = PiePlot(theme=theme)
470
+ plotter.plot(
471
+ data=df,
472
+ x=x,
473
+ y=y,
474
+ title=title,
475
+ autopct=autopct,
476
+ startangle=startangle,
477
+ explode=explode_arg,
478
+ shadow=shadow,
479
+ output=str(output),
480
+ )
481
+ typer.echo(f"Saved pie chart to {output}")
482
+
483
+ @app.command("gsea")
484
+ def _gsea(
485
+ input_file: Path = typer.Argument(
486
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
487
+ ),
488
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
489
+ rank: str = typer.Option("rank", help="zh: 排名列名 (X 轴)。\nen: Column name for rank (x-axis)."),
490
+ score: str = typer.Option(
491
+ "running_es", help="zh: 运行富集得分列名 (Y 轴)。\nen: Column name for running ES (y-axis)."
492
+ ),
493
+ hit: str = typer.Option(
494
+ "hit", help="zh: 命中状态列名 (0/1 或布尔值)。\nen: Column name for hit status (0/1 or boolean)."
495
+ ),
496
+ metric: str = typer.Option(
497
+ None, help="zh: 排名指标列名 (Y 轴底部)。\nen: Column name for ranking metric (y-axis bottom)."
498
+ ),
499
+ theme: str = typer.Option(
500
+ "nature",
501
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
502
+ ),
503
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
504
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
505
+ color: str = typer.Option("#4DAF4A", help="zh: 富集得分曲线颜色。\nen: Color for enrichment score line."),
506
+ hit_color: str = typer.Option("black", help="zh: 命中线条颜色。\nen: Color for hit lines."),
507
+ nes: float = typer.Option(None, help="zh: 标准化富集得分 (NES)。\nen: Normalized Enrichment Score."),
508
+ pvalue: float = typer.Option(None, help="zh: P-value。\nen: P-value."),
509
+ fdr: float = typer.Option(None, help="zh: FDR q-value。\nen: FDR q-value."),
510
+ show_border: bool = typer.Option(True, help="zh: 显示顶部和右侧边框。\nen: Show top and right borders."),
511
+ ) -> None:
512
+ """
513
+ zh: 生成 GSEA 富集图。
514
+ en: Generate GSEA enrichment plot.
515
+ """
516
+ df = read_data(input_file, sheet=sheet)
517
+ plotter = GSEAPlot(theme=theme)
518
+ plotter.plot(
519
+ data=df,
520
+ rank=rank,
521
+ score=score,
522
+ hit=hit,
523
+ metric=metric,
524
+ title=title,
525
+ output=str(output),
526
+ color=color,
527
+ hit_color=hit_color,
528
+ nes=nes,
529
+ pvalue=pvalue,
530
+ fdr=fdr,
531
+ show_border=show_border,
532
+ )
533
+ typer.echo(f"Saved GSEA plot to {output}")
534
+
535
+ @app.command("chromosome")
536
+ def _chromosome(
537
+ input_file: Path = typer.Argument(
538
+ ..., help="zh: 输入文件路径 (CSV, TSV 或 Excel)。\nen: Input file path (CSV, TSV, or Excel)."
539
+ ),
540
+ output: Path = typer.Option(..., "-o", "--output", help="zh: 输出文件路径。\nen: Output file path."),
541
+ chrom_col: str = typer.Option("chrom", help="zh: 染色体列名。\nen: Column name for chromosome."),
542
+ pos_col: str = typer.Option("pos", help="zh: 位置列名。\nen: Column name for position."),
543
+ pos_counts_col: str = typer.Option(
544
+ "pos_counts", help="zh: 正链计数列名。\nen: Column name for positive strand counts."
545
+ ),
546
+ neg_counts_col: str = typer.Option(
547
+ "neg_counts", help="zh: 负链计数列名。\nen: Column name for negative strand counts."
548
+ ),
549
+ theme: str = typer.Option(
550
+ "nature",
551
+ help="zh: 绘图主题 (nature, science, default) 或自定义主题路径 (.json/.py)。\nen: Plot theme (nature, science, default) or path to custom theme (.json/.py).",
552
+ ),
553
+ title: str = typer.Option(None, help="zh: 图表标题。\nen: Plot title."),
554
+ sheet: str = typer.Option(None, help="zh: Excel 工作表名称。\nen: Sheet name for Excel files."),
555
+ max_chroms: int = typer.Option(
556
+ 24, help="zh: 显示的最大染色体数量。\nen: Maximum number of chromosomes to display."
557
+ ),
558
+ ) -> None:
559
+ """
560
+ zh: 生成染色体覆盖度分布图。
561
+ en: Generate chromosome coverage distribution plot.
562
+ """
563
+ df = read_data(input_file, sheet=sheet)
564
+ plotter = ChromosomePlot(theme=theme)
565
+ plotter.plot(
566
+ data=df,
567
+ chrom_col=chrom_col,
568
+ pos_col=pos_col,
569
+ pos_counts_col=pos_counts_col,
570
+ neg_counts_col=neg_counts_col,
571
+ max_chroms=max_chroms,
572
+ title=title,
573
+ output=str(output),
574
+ )
575
+ typer.echo(f"Saved chromosome plot to {output}")
576
+
577
+ localize_app(app, detect_language())
578
+ return app
@@ -0,0 +1,30 @@
1
+ """
2
+ zh: 图表类集合。
3
+ en: Collection of chart classes.
4
+ """
5
+
6
+ from .bar import BarPlot
7
+ from .base import BasePlot
8
+ from .box import BoxPlot
9
+ from .chromosome import ChromosomePlot
10
+ from .gsea import GSEAPlot
11
+ from .heatmap import HeatmapPlot
12
+ from .line import LinePlot
13
+ from .pca import PCAPlot
14
+ from .pie import PiePlot
15
+ from .scatter import ScatterPlot
16
+ from .volcano import VolcanoPlot
17
+
18
+ __all__ = [
19
+ "BarPlot",
20
+ "BasePlot",
21
+ "BoxPlot",
22
+ "ChromosomePlot",
23
+ "GSEAPlot",
24
+ "HeatmapPlot",
25
+ "LinePlot",
26
+ "PCAPlot",
27
+ "PiePlot",
28
+ "ScatterPlot",
29
+ "VolcanoPlot",
30
+ ]