rsplotlib 0.1.9__cp313-cp313-win_amd64.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.
rsplotlib/pyplot.py ADDED
@@ -0,0 +1,1194 @@
1
+ """rsplotlib.pyplot - Matplotlib pyplot 兼容接口
2
+
3
+ 此模块提供与 matplotlib.pyplot 兼容的 API,所有函数代理到 rsplotlib 核心模块。
4
+ 使用方法: import rsplotlib.pyplot as plt
5
+ """
6
+
7
+ from . import rsplotlib as _rsplotlib
8
+ from .figure._defaults import DEFAULT_DPI, DEFAULT_FIGSIZE
9
+ # ============ 样式接口 ============
10
+ from .utils import style as _style_module
11
+
12
+ # 延迟获取 mpl.rcParams,避免 pyplot <-> pylab 循环导入
13
+
14
+
15
+ def _get_rcparams():
16
+ """从 pylab.mpl 获取 rcParams,统一配置入口"""
17
+ from .pylab import mpl
18
+ return mpl.rcParams
19
+
20
+
21
+ # ==================== 内部辅助函数 ====================
22
+
23
+ def _to_list(obj):
24
+ """将 numpy 数组或其他可迭代对象转换为 Python list
25
+
26
+ 支持 numpy ndarray、Python list、tuple 及其他可迭代对象。
27
+ 标量值直接返回。
28
+ """
29
+ if obj is None:
30
+ return None
31
+ # numpy ndarray
32
+ if hasattr(obj, 'tolist'):
33
+ return obj.tolist()
34
+ # Python list/tuple 或其他可迭代对象
35
+ if isinstance(obj, (list, tuple)):
36
+ return list(obj)
37
+ # 标量
38
+ return obj
39
+
40
+
41
+ def _to_list_recursive(obj):
42
+ """递归转换嵌套的 numpy 数组为 Python list"""
43
+ if obj is None:
44
+ return None
45
+ if hasattr(obj, 'tolist'):
46
+ return obj.tolist()
47
+ if isinstance(obj, (list, tuple)):
48
+ return [_to_list_recursive(item) for item in obj]
49
+ return obj
50
+
51
+
52
+ def _get_axes():
53
+ """获取当前 axes,如果没有则返回 None"""
54
+ try:
55
+ return _rsplotlib.gca()
56
+ except Exception:
57
+ return None
58
+
59
+
60
+ def _get_figure():
61
+ """获取当前 figure,如果没有则返回 None"""
62
+ try:
63
+ return _rsplotlib.gcf()
64
+ except Exception:
65
+ return None
66
+
67
+
68
+ def _route_to_ax(ax_method_name, module_method, *args, **kwargs):
69
+ """将调用路由到当前 axes(如果存在)或模块级函数
70
+
71
+ Args:
72
+ ax_method_name: axes 的方法名(字符串)
73
+ module_method: 模块级函数(可调用对象)
74
+ *args: 传递给方法的参数
75
+ **kwargs: 关键字参数(必须同时转发到 axes 端,否则会丢参)
76
+ """
77
+ ax = _get_axes()
78
+ if ax is not None and hasattr(ax, ax_method_name):
79
+ method = getattr(ax, ax_method_name)
80
+ method(*args, **kwargs)
81
+ return _get_figure()
82
+ return module_method(*args, **kwargs)
83
+
84
+
85
+ def _map_aliases(kwargs):
86
+ """规范化 matplotlib 别名到标准名"""
87
+ alias_map = {
88
+ 'lw': 'linewidth',
89
+ 'c': 'color',
90
+ 'ls': 'linestyle',
91
+ 'ms': 'markersize',
92
+ 'mfc': 'markerfacecolor',
93
+ 'mec': 'markeredgecolor',
94
+ 'mew': 'markeredgewidth',
95
+ }
96
+ for alias, target in alias_map.items():
97
+ if alias in kwargs and target not in kwargs:
98
+ kwargs[target] = kwargs.pop(alias)
99
+ elif alias in kwargs:
100
+ kwargs.pop(alias)
101
+ # 规范化 linestyle 词形 ('solid'/'dotted'/'dashed'/'dashdot') 与空值到简写,
102
+ # 与 matplotlib 一致:既可写 linestyle='dotted' 也可写 linestyle=':'。
103
+ ls = kwargs.get('linestyle')
104
+ if isinstance(ls, str):
105
+ key = ls.strip().lower()
106
+ if key == '' or key == 'none':
107
+ kwargs['linestyle'] = ' ' # 空串 / ' ' / 'None' 均表示不画线
108
+ elif key in _LINESTYLE_ALIASES:
109
+ kwargs['linestyle'] = _LINESTYLE_ALIASES[key]
110
+ # 已是简写 ('-' / '--' / ':' / '-.') 时保持不变
111
+
112
+
113
+ # linestyle 词形 -> 简写。空串 / 'None' 在 _map_aliases 中单独处理为 ' '(不画线)。
114
+ _LINESTYLE_ALIASES = {
115
+ 'solid': '-',
116
+ 'dotted': ':',
117
+ 'dashed': '--',
118
+ 'dashdot': '-.',
119
+ }
120
+
121
+
122
+ def _parse_fmt(fmt):
123
+ """解析 matplotlib 风格的格式字符串 '[marker][line][color]'。
124
+
125
+ fmt 由三部分任意组合而成 (均可省略):
126
+ marker: 数据点标记, 如 'o' 圆, '^' 三角, 's' 方块, '*' 星 ...
127
+ line: 线型, '-' 实线, '--' 虚线, '-.' 点划线, ':' 点线
128
+ color: 颜色单字母代码 b/g/r/c/m/y/k/w
129
+
130
+ 例如 'o:r' = 圆形标记 + 点线 + 红色。
131
+
132
+ 注意 black 与 blue 首字母冲突: 按 matplotlib 约定,
133
+ 单字母代码里 'b' 表示 blue, 'k' 才表示 black, 因此不会产生歧义。
134
+
135
+ 返回 dict, 可能包含 'marker' / 'linestyle' / 'color' 键。
136
+ """
137
+ if not fmt:
138
+ return {}
139
+ line_styles_multi = ('--', '-.')
140
+ line_styles_single = ('-', ':')
141
+ markers = set(".,ov^<>12348spP*hH+xXDd|_")
142
+ color_map = {
143
+ 'b': 'blue', 'g': 'green', 'r': 'red', 'c': 'cyan',
144
+ 'm': 'magenta', 'y': 'yellow', 'k': 'black', 'w': 'white',
145
+ }
146
+
147
+ marker = linestyle = color = None
148
+ i, n = 0, len(fmt)
149
+ while i < n:
150
+ two = fmt[i:i + 2]
151
+ ch = fmt[i]
152
+ if two in line_styles_multi:
153
+ if linestyle is not None:
154
+ raise ValueError(f"格式字符串 {fmt!r} 中出现了重复的线型")
155
+ linestyle, i = two, i + 2
156
+ elif ch in line_styles_single:
157
+ if linestyle is not None:
158
+ raise ValueError(f"格式字符串 {fmt!r} 中出现了重复的线型")
159
+ linestyle, i = ch, i + 1
160
+ elif ch in markers:
161
+ if marker is not None:
162
+ raise ValueError(f"格式字符串 {fmt!r} 中出现了重复的标记")
163
+ marker, i = ch, i + 1
164
+ elif ch in color_map:
165
+ if color is not None:
166
+ raise ValueError(f"格式字符串 {fmt!r} 中出现了重复的颜色")
167
+ color, i = color_map[ch], i + 1
168
+ else:
169
+ raise ValueError(f"无法识别的格式字符串 {fmt!r} (非法字符 {ch!r})")
170
+
171
+ result = {}
172
+ if marker is not None:
173
+ result['marker'] = marker
174
+ if linestyle is not None:
175
+ result['linestyle'] = linestyle
176
+ if color is not None:
177
+ result['color'] = color
178
+ return result
179
+
180
+
181
+ def _parse_plot_args(args, kwargs):
182
+ """解析 plot() 的位置参数为 [(x, y, fmt_or_none), ...] 对列表 + kwargs
183
+
184
+ 支持的调用形式 (与 matplotlib 一致):
185
+ plot(y) plot(x, y)
186
+ plot(y, fmt) plot(x, y, fmt)
187
+ plot(x1, y1, fmt1, x2, y2, fmt2, ...) # 多条线, 每组 fmt 可选
188
+ """
189
+ args = list(args)
190
+ pairs = []
191
+ while args:
192
+ # 取出本组数据参数 (1~2 个); 若第 2 个其实是 fmt 字符串, 则本组只有 1 个
193
+ group = args[:2]
194
+ if len(group) == 2 and isinstance(group[1], str):
195
+ group = group[:1]
196
+ args = args[len(group):]
197
+ # 消费紧跟其后的 fmt 字符串 (若有)
198
+ fmt = None
199
+ if args and isinstance(args[0], str):
200
+ fmt, args = args[0], args[1:]
201
+
202
+ if len(group) == 1:
203
+ y = group[0]
204
+ try:
205
+ x = list(range(len(y)))
206
+ except Exception:
207
+ x = list(y) if hasattr(y, '__iter__') else []
208
+ pairs.append((x, y, fmt))
209
+ else:
210
+ pairs.append((group[0], group[1], fmt))
211
+ return pairs, kwargs
212
+
213
+
214
+ # ==================== 绘图函数 ====================
215
+
216
+ def plot(*args, **kwargs):
217
+ """绘制折线图。
218
+
219
+ 用法:
220
+ plt.plot(x, y) # 以 x 为横坐标, y 为纵坐标
221
+ plt.plot(y) # 仅提供 y, 自动 x = [0, 1, ...]
222
+ plt.plot(x, y, lw=2.0) # 自定义线宽
223
+ plt.plot(x, y, x, z) # 绘制多条线
224
+ plt.plot(y, 'o:r') # 格式字符串: 圆标记 + 点线 + 红色
225
+
226
+ 格式字符串 fmt = '[marker][line][color]', 各部分均可省略, 例如:
227
+ 'o' 仅圆形标记 '--' 仅虚线
228
+ 'o:r' 圆标记+点线+红色 '^-g' 三角标记+实线+绿色
229
+ 颜色单字母: b=blue g=green r=red c=cyan m=magenta y=yellow k=black w=white
230
+ (注意 black 用 'k' 而非 'b', 'b' 表示 blue, 避免与 blue 首字母冲突)
231
+ 显式关键字参数优先于 fmt 中的同名设置。
232
+
233
+ 关键字参数 (matplotlib 兼容别名):
234
+ lw / linewidth: 线宽 (float)
235
+ c / color: 颜色 (如 'red', '#FF0000')
236
+ ls / linestyle: 线型 ('-', '--', ':', '-.')
237
+ marker: 数据点标记 ('o', 's', '^', 'D', '*', 'x', '+')
238
+ ms / markersize: 标记大小 (float)
239
+ mfc / markerfacecolor: 标记内部填充色
240
+ mec / markeredgecolor: 标记边框色
241
+ solid_capstyle: 端点 ('butt', 'round', 'projecting')
242
+ label: 图例标签
243
+
244
+ Returns:
245
+ (Figure, Axes) 元组
246
+ """
247
+ _map_aliases(kwargs)
248
+ pairs, _ = _parse_plot_args(args, kwargs)
249
+
250
+ def _call(*a, **k):
251
+ return _rsplotlib.plot(*a, **k)
252
+
253
+ result = None
254
+ for x, y, fmt in pairs:
255
+ call_kwargs = dict(kwargs)
256
+ if fmt:
257
+ # fmt 解析出的样式作为默认值, 不覆盖用户显式传入的关键字参数
258
+ for key, value in _parse_fmt(fmt).items():
259
+ call_kwargs.setdefault(key, value)
260
+ result = _route_to_ax('plot', _call, x, y, **call_kwargs)
261
+ return result
262
+
263
+
264
+ def scatter(x, y, s=20.0, c=None, marker='o', label=None, alpha=1.0, **kwargs):
265
+ """绘制散点图。
266
+
267
+ 支持每个点独立的颜色和大小:
268
+ plt.scatter(x, y, s=50, c='red') # 统一大小和颜色
269
+ plt.scatter(x, y, s=[10, 20, 30], c=['red', 'green', 'blue'])
270
+
271
+ Args:
272
+ x: x 坐标 (list / tuple / numpy array)
273
+ y: y 坐标 (list / tuple / numpy array)
274
+ s: 点大小, 单个浮点数 或 浮点数数组
275
+ c: 颜色, 单个字符串 或 颜色字符串数组
276
+ marker: 标记形状 ('o', 's', '^', 'D', '*', 'x', '+')
277
+ label: 图例标签
278
+ alpha: 透明度 (0.0 - 1.0)
279
+ **kwargs: 额外关键字参数 (color 将作为 c 的别名)
280
+ """
281
+ x = _to_list(x)
282
+ y = _to_list(y)
283
+ # 支持 color 作为 c 的别名
284
+ if c is None and 'color' in kwargs:
285
+ c = kwargs.pop('color')
286
+ # 如果 s 或 c 是数组, 则路由到 scatter_multi (Rust 层批量实现)
287
+ if isinstance(s, (list, tuple)) or (isinstance(c, (list, tuple)) and c and isinstance(c[0], str)):
288
+ return _route_to_ax('scatter_multi', _rsplotlib.scatter_multi, x, y, s, c, marker, label, alpha)
289
+ return _route_to_ax('scatter', _rsplotlib.scatter, x, y, s, c, marker, label, alpha)
290
+
291
+
292
+ def bar(x, height, width=0.8, color=None, label=None):
293
+ """绘制柱状图。
294
+
295
+ Args:
296
+ x: 每个柱子的 x 坐标 (list / tuple / numpy array)
297
+ height: 每个柱子的高度 (y 值)
298
+ width: 柱子的宽度 (默认 0.8)
299
+ color: 柱子的颜色字符串
300
+ label: 图例标签
301
+
302
+ 用法:
303
+ plt.bar([0, 1, 2], [1, 2, 3])
304
+ """
305
+ x = _to_list(x)
306
+ height = _to_list(height)
307
+ return _route_to_ax('bar', _rsplotlib.bar, x, height, width, color, label)
308
+
309
+
310
+ def barh(y, width, height=0.8, color=None, label=None):
311
+ """绘制水平柱状图。
312
+
313
+ Args:
314
+ y: 每个柱子的 y 坐标
315
+ width: 每个柱子的宽度 (x 方向长度)
316
+ height: 柱子的高度 (y 方向, 默认 0.8)
317
+ color: 颜色
318
+ label: 图例标签
319
+ """
320
+ y = _to_list(y)
321
+ width = _to_list(width)
322
+ return _route_to_ax('barh', _rsplotlib.barh, y, width, height, color, label)
323
+
324
+
325
+ def hist(x, bins=10, density=False, label=None, alpha=0.7, color=None, **kwargs):
326
+ """绘制直方图。
327
+
328
+ 用法:
329
+ plt.hist(data, bins=20)
330
+ plt.hist([data1, data2], bins=10, color=['red', 'blue'])
331
+
332
+ Args:
333
+ x: 数据 (一维数组, 或多组数据组成的列表)
334
+ bins: 分箱数量 (默认 10)
335
+ density: 是否归一化到概率密度 (默认 False)
336
+ label: 图例标签
337
+ alpha: 透明度 (默认 0.7)
338
+ color: 颜色或颜色列表
339
+ **kwargs: 额外关键字参数 (facecolor, align, histtype)
340
+ """
341
+ facecolor = kwargs.pop('facecolor', None)
342
+ align = kwargs.pop('align', None)
343
+ histtype = kwargs.pop('histtype', None)
344
+ _color = facecolor if facecolor is not None else color
345
+
346
+ x = _to_list_recursive(x)
347
+ if x and isinstance(x[0], (list, tuple)):
348
+ x_list = [list(v) for v in x]
349
+ else:
350
+ x_list = [list(x)]
351
+
352
+ if _color is not None:
353
+ if isinstance(_color, str):
354
+ color_list = [_color] * len(x_list)
355
+ elif isinstance(_color, (list, tuple)):
356
+ color_list = list(_color)
357
+ else:
358
+ color_list = None
359
+ else:
360
+ color_list = None
361
+
362
+ def _call_hist(*a, **k):
363
+ return _rsplotlib.hist(*a, **k)
364
+
365
+ return _route_to_ax('hist', _call_hist, x_list, bins=bins, density=density,
366
+ label=label, alpha=alpha, color=color_list,
367
+ facecolor=None, align=align, histtype=histtype)
368
+
369
+
370
+ def pie(x, labels=None, colors=None, autopct=False, **kwargs):
371
+ """绘制饼图。
372
+
373
+ 用法:
374
+ plt.pie([30, 40, 30], labels=['A', 'B', 'C'])
375
+
376
+ Args:
377
+ x: 数据列表 (各部分数值)
378
+ labels: 每部分的标签列表
379
+ colors: 每部分的颜色列表
380
+ autopct: 百分比格式字符串 (如 '%1.1f%%'), 或布尔值 True
381
+ **kwargs: 其他关键字参数
382
+ """
383
+ x = _to_list(x)
384
+ if autopct and isinstance(autopct, str):
385
+ autopct_str = autopct
386
+ elif autopct:
387
+ autopct_str = "%1.1f%%"
388
+ else:
389
+ autopct_str = None
390
+ return _route_to_ax('pie', _rsplotlib.pie, x, labels, colors, autopct_str)
391
+
392
+
393
+ def boxplot(x, labels=None, vert=True, **kwargs):
394
+ """绘制箱线图 (box-and-whisker plot)。
395
+
396
+ 展示数据的中位数、四分位、离群值等统计信息。
397
+
398
+ 用法:
399
+ plt.boxplot([data1, data2, data3])
400
+
401
+ Args:
402
+ x: 数据集 (可以是一个或多个一维数组)
403
+ labels: 每个箱的标签列表
404
+ vert: 是否垂直绘制 (默认 True)
405
+ """
406
+ x = _to_list_recursive(x)
407
+ return _route_to_ax('boxplot', _rsplotlib.boxplot, x, labels, vert)
408
+
409
+
410
+ def fill_between(x, y1, y2=0.0, color=None, alpha=0.3, label=None, **kwargs):
411
+ """填充两条曲线之间的区域。
412
+
413
+ 用法:
414
+ plt.fill_between(x, y1, y2, color='red', alpha=0.3)
415
+
416
+ Args:
417
+ x: x 坐标数据
418
+ y1: 第一条曲线的 y 坐标
419
+ y2: 第二条曲线的 y 坐标 (默认 0.0)
420
+ color: 填充颜色
421
+ alpha: 透明度 (0.0-1.0, 默认 0.3)
422
+ label: 图例标签
423
+ """
424
+ x = _to_list(x)
425
+ y1 = _to_list(y1)
426
+ y2 = _to_list(y2)
427
+ return _route_to_ax('fill_between', _rsplotlib.fill_between, x, y1, y2, color, alpha, label)
428
+
429
+
430
+ def errorbar(x, y, yerr=None, xerr=None, fmt='o', color=None, label=None, capsize=3.0, **kwargs):
431
+ """绘制带误差棒的图。
432
+
433
+ 用法:
434
+ plt.errorbar(x, y, yerr=0.1)
435
+
436
+ Args:
437
+ x: x 坐标数据
438
+ y: y 坐标数据
439
+ yerr: y 方向误差 (标量或数组)
440
+ xerr: x 方向误差 (标量或数组)
441
+ fmt: 数据点/线格式字符串 (默认 'o')
442
+ color: 颜色
443
+ label: 图例标签
444
+ capsize: 误差棒末端横线长度 (默认 3.0)
445
+ """
446
+ x = _to_list(x)
447
+ y = _to_list(y)
448
+ yerr = _to_list(yerr)
449
+ xerr = _to_list(xerr)
450
+ return _route_to_ax('errorbar', _rsplotlib.errorbar, x, y, yerr, xerr, fmt, color, label, capsize)
451
+
452
+
453
+ def stem(x, y, linefmt=None, markerfmt=None, label=None, **kwargs):
454
+ """绘制茎叶图 (火柴杆图)。
455
+
456
+ Args:
457
+ x: x 坐标数据
458
+ y: y 坐标数据
459
+ linefmt: 线样式
460
+ markerfmt: 标记样式
461
+ label: 图例标签
462
+ """
463
+ x = _to_list(x)
464
+ y = _to_list(y)
465
+ return _route_to_ax('stem', _rsplotlib.stem, x, y, linefmt or '-', markerfmt or 'o', label)
466
+
467
+
468
+ def step(x, y, where='pre', label=None, color=None, linestyle='-', linewidth=1.5, **kwargs):
469
+ """绘制阶梯图。
470
+
471
+ Args:
472
+ x: x 坐标数据
473
+ y: y 坐标数据
474
+ where: 阶梯位置 ('pre', 'mid', 'post', 默认 'pre')
475
+ label: 图例标签
476
+ color: 颜色
477
+ linestyle: 线型
478
+ linewidth: 线宽
479
+ """
480
+ x = _to_list(x)
481
+ y = _to_list(y)
482
+ return _route_to_ax('step', _rsplotlib.step, x, y, where, label, color, linestyle, linewidth)
483
+
484
+
485
+ def stackplot(x, *args, labels=None, colors=None, alpha=1.0, **kwargs):
486
+ """绘制堆叠面积图。
487
+
488
+ 用法:
489
+ plt.stackplot(x, y1, y2, y3, labels=['A', 'B', 'C'])
490
+
491
+ Args:
492
+ x: x 坐标数据
493
+ *args: 多个 y 数据集
494
+ labels: 每个数据集的标签列表
495
+ colors: 每个数据集的颜色列表
496
+ alpha: 透明度 (默认 1.0)
497
+ """
498
+ x = _to_list(x)
499
+ y_data = [list(a) for a in args if a is not None]
500
+ if not y_data and 'y' in kwargs:
501
+ y_data = [_to_list(kwargs['y'])]
502
+ return _route_to_ax('stackplot', _rsplotlib.stackplot, x, *y_data,
503
+ labels=labels, colors=colors, alpha=alpha)
504
+
505
+
506
+ def imshow(x, cmap='viridis', aspect='auto', **kwargs):
507
+ """显示图像 (矩阵热力图)。
508
+
509
+ Args:
510
+ x: 2D 数组 (行对应 y 轴, 列对应 x 轴)
511
+ cmap: 颜色映射名称 (默认 'viridis')
512
+ aspect: 宽高比 ('auto', 'equal', 或数值)
513
+ """
514
+ x = _to_list_recursive(x)
515
+ return _route_to_ax('imshow', _rsplotlib.imshow, x, cmap, aspect)
516
+
517
+
518
+ def semilogx(x, y, label=None, color=None, linestyle=None, marker=None, linewidth=None, **kwargs):
519
+ """绘制 x 轴对数刻度图。"""
520
+ x = _to_list(x)
521
+ y = _to_list(y)
522
+ return _rsplotlib.semilogx(x, y, label, color, linestyle, marker, linewidth)
523
+
524
+
525
+ def semilogy(x, y, label=None, color=None, linestyle=None, marker=None, linewidth=None, **kwargs):
526
+ """绘制 y 轴对数刻度图。"""
527
+ x = _to_list(x)
528
+ y = _to_list(y)
529
+ return _rsplotlib.semilogy(x, y, label, color, linestyle, marker, linewidth)
530
+
531
+
532
+ def loglog(x, y, label=None, color=None, linestyle=None, marker=None, linewidth=None, **kwargs):
533
+ """绘制双对数刻度图。"""
534
+ x = _to_list(x)
535
+ y = _to_list(y)
536
+ return _rsplotlib.loglog(x, y, label, color, linestyle, marker, linewidth)
537
+
538
+
539
+ # ==================== 辅助元素 ====================
540
+
541
+ def text(x, y, s, fontdict=None, **kwargs):
542
+ """添加文本标注。
543
+
544
+ Args:
545
+ x, y: 文本位置 (数据坐标)
546
+ s: 文本内容
547
+ fontdict: 字体属性字典 (可选)
548
+ **kwargs: 支持 fontsize, color/c, family 等参数
549
+ """
550
+ fontsize = kwargs.get('fontsize', fontdict.get('fontsize', 12) if fontdict else 12)
551
+ color = kwargs.get('color', fontdict.get('color', 'black') if fontdict else 'black')
552
+ c = kwargs.get('c', None)
553
+ family = kwargs.get('family', None)
554
+ if not isinstance(s, str):
555
+ s = str(s)
556
+
557
+ # family 处理:若用户显式指定了字体族名,解析为本地字体文件并注册到
558
+ # plotters 的字体数据库,这样真正驱动文本渲染(而不是被忽略)。
559
+ if family:
560
+ try:
561
+ import os
562
+ from .utils._font_resolver import resolve_font_path
563
+ path = resolve_font_path(family)
564
+ if path is None and os.path.isfile(family):
565
+ path = family # 也允许直接传文件路径
566
+ if path is not None:
567
+ _rsplotlib.register_sans_serif_font(path)
568
+ except Exception:
569
+ # 字体注册失败不影响绘制(会回退到默认 sans-serif)
570
+ pass
571
+
572
+ return _rsplotlib.text(x, y, s, fontsize, color, c, family)
573
+
574
+
575
+ def axhline(y=0, **kwargs):
576
+ """绘制水平参考线。
577
+
578
+ Args:
579
+ y: y 坐标位置
580
+ color: 线颜色
581
+ linestyle: 线型 ('-', '--', ':', '-.')
582
+ linewidth: 线宽
583
+ """
584
+ return _rsplotlib.axhline(y, kwargs.get('color'), kwargs.get('linestyle'), kwargs.get('linewidth'))
585
+
586
+
587
+ def axvline(x=0, **kwargs):
588
+ """绘制垂直参考线。
589
+
590
+ Args:
591
+ x: x 坐标位置
592
+ color: 线颜色
593
+ linestyle: 线型
594
+ linewidth: 线宽
595
+ """
596
+ return _rsplotlib.axvline(x, kwargs.get('color'), kwargs.get('linestyle'), kwargs.get('linewidth'))
597
+
598
+
599
+ def axhspan(ymin, ymax, **kwargs):
600
+ """绘制水平区间填充 (在 y 方向高亮一个区间)。
601
+
602
+ 用法:
603
+ plt.axhspan(0.0, 1.0, color='yellow', alpha=0.3)
604
+
605
+ Args:
606
+ ymin: y 轴下限
607
+ ymax: y 轴上限
608
+ color: 填充颜色 (默认蓝灰色)
609
+ alpha: 透明度 (0.0-1.0, 默认 0.3)
610
+ **kwargs: 其他关键字参数
611
+ """
612
+ return _rsplotlib.axhspan(ymin, ymax, kwargs.get('color'), kwargs.get('alpha', 0.3))
613
+
614
+
615
+ def axvspan(xmin, xmax, **kwargs):
616
+ """绘制垂直区间填充 (在 x 方向高亮一个区间)。
617
+
618
+ 用法:
619
+ plt.axvspan(0.0, 1.0, color='yellow', alpha=0.3)
620
+
621
+ Args:
622
+ xmin: x 轴下限
623
+ xmax: x 轴上限
624
+ color: 填充颜色 (默认蓝灰色)
625
+ alpha: 透明度 (0.0-1.0, 默认 0.3)
626
+ **kwargs: 其他关键字参数
627
+ """
628
+ return _rsplotlib.axvspan(xmin, xmax, kwargs.get('color'), kwargs.get('alpha', 0.3))
629
+
630
+
631
+ def axline(xy1, xy2, **kwargs):
632
+ """通过两点绘制任意斜率的直线 (延长到整个绘图区域)。
633
+
634
+ 用法:
635
+ plt.axline((0, 0), (1, 1), color='red')
636
+
637
+ Args:
638
+ xy1: 起点坐标 (x1, y1)
639
+ xy2: 终点坐标 (x2, y2)
640
+ color: 线颜色
641
+ linestyle: 线型
642
+ linewidth: 线宽
643
+ **kwargs: 其他关键字参数
644
+ """
645
+ return _rsplotlib.axline(
646
+ tuple(xy1), tuple(xy2),
647
+ kwargs.get('color'), kwargs.get('linestyle'),
648
+ kwargs.get('linewidth'),
649
+ )
650
+
651
+
652
+ def annotate(text, xy, xytext=None, fontsize=12.0, color='black', arrowprops=None, **kwargs):
653
+ """在指定坐标添加文本标注, 可选带箭头。
654
+
655
+ 用法:
656
+ plt.annotate('重要点', xy=(1, 2), xytext=(3, 4),
657
+ arrowprops=dict(arrowstyle='->'))
658
+
659
+ Args:
660
+ text: 标注文本内容
661
+ xy: 被标注点的坐标 (数据坐标)
662
+ xytext: 文本放置位置 (数据坐标)。若提供, 自动从该位置绘制箭头到 xy
663
+ fontsize: 字体大小 (默认 12.0)
664
+ color: 文本和箭头颜色
665
+ arrowprops: 箭头属性字典 (支持 arrowstyle, arrowsize 等)
666
+ **kwargs: 其他关键字参数
667
+ """
668
+ arrowstyle = None
669
+ arrowsize = 1.0
670
+ if arrowprops is not None:
671
+ if isinstance(arrowprops, dict):
672
+ if 'arrowstyle' in arrowprops:
673
+ arrowstyle = arrowprops['arrowstyle']
674
+ if 'arrowsize' in arrowprops:
675
+ arrowsize = arrowprops['arrowsize']
676
+ ax = _get_axes()
677
+ if ax is not None and hasattr(ax, 'annotate'):
678
+ ax.annotate(text, xy, xytext, fontsize, color, arrowprops, arrowstyle, arrowsize)
679
+ return _get_figure()
680
+ fig, ax = _rsplotlib.subplots()
681
+ ax.annotate(text, xy, xytext, fontsize, color, arrowprops, arrowstyle, arrowsize)
682
+ return fig, ax
683
+
684
+
685
+ def hlines(y, xmin=None, xmax=None, **kwargs):
686
+ """在指定 y 位置绘制多条水平线段。
687
+
688
+ 由 Rust 层批量实现, 避免 Python 级 for 循环。
689
+
690
+ Args:
691
+ y: 单个 y 值 或 多个 y 值的列表
692
+ color: 线颜色
693
+ linestyle: 线型
694
+ linewidth: 线宽
695
+ **kwargs: 其他关键字参数
696
+ """
697
+ y_arr = _to_list(y) if isinstance(y, (list, tuple)) or hasattr(y, 'tolist') else [y]
698
+ ax = _get_axes()
699
+ if ax is not None and hasattr(ax, 'hlines'):
700
+ ax.hlines(y_arr, kwargs.get('color'), kwargs.get('linestyle'), kwargs.get('linewidth'))
701
+ return _get_figure()
702
+ return _rsplotlib.hlines(y_arr, kwargs.get('color'), kwargs.get('linestyle'), kwargs.get('linewidth'))
703
+
704
+
705
+ def vlines(x, ymin=None, ymax=None, **kwargs):
706
+ """在指定 x 位置绘制多条垂直线段 (Rust 层批量实现)。
707
+
708
+ Args:
709
+ x: 单个 x 值 或 多个 x 值的列表
710
+ color: 线颜色
711
+ linestyle: 线型
712
+ linewidth: 线宽
713
+ **kwargs: 其他关键字参数
714
+ """
715
+ x_arr = _to_list(x) if isinstance(x, (list, tuple)) or hasattr(x, 'tolist') else [x]
716
+ ax = _get_axes()
717
+ if ax is not None and hasattr(ax, 'vlines'):
718
+ ax.vlines(x_arr, kwargs.get('color'), kwargs.get('linestyle'), kwargs.get('linewidth'))
719
+ return _get_figure()
720
+ return _rsplotlib.vlines(x_arr, kwargs.get('color'), kwargs.get('linestyle'), kwargs.get('linewidth'))
721
+
722
+
723
+ # ==================== 配置函数 ====================
724
+
725
+ def _font_props(fontdict, kwargs):
726
+ """从 fontdict 与关键字参数中提取字体属性 (family, size, color)。
727
+
728
+ matplotlib 语义:关键字参数优先于 fontdict;family 支持 family/fontfamily/fontname
729
+ 别名,size 支持 size/fontsize,color 支持 color/c。返回 (family, size, color),
730
+ 其中 size 已转为 float 或 None。
731
+ """
732
+ fd = fontdict or {}
733
+
734
+ def _pick(*keys):
735
+ for k in keys:
736
+ if kwargs.get(k) is not None:
737
+ return kwargs[k]
738
+ for k in keys:
739
+ if fd.get(k) is not None:
740
+ return fd[k]
741
+ return None
742
+
743
+ family = _pick('family', 'fontfamily', 'fontname')
744
+ size = _pick('size', 'fontsize')
745
+ color = _pick('color', 'c')
746
+ size = float(size) if size is not None else None
747
+ return family, size, color
748
+
749
+
750
+ def xlabel(text, fontdict=None, loc=None, **kwargs):
751
+ """设置 x 轴标签文本,并可通过 fontdict / 关键字参数自定义字体属性。
752
+
753
+ 支持的字体属性 (fontdict 的键或直接关键字参数, 关键字参数优先):
754
+ family / fontfamily / fontname: 字体族名 (如 'Courier'、'STHeiti Light' 等)
755
+ size / fontsize: 字号 (points)
756
+ color: 文本颜色 (如 'r'、'#ff0000'、'SeaGreen')
757
+
758
+ loc: 标签水平位置,可选 'left'、'center'、'right',默认 'center'。
759
+
760
+ 用法:
761
+ plt.xlabel("x - label")
762
+ plt.xlabel("x 轴", fontdict={"family": "STHeiti Light", "size": 16, "color": "b"})
763
+ plt.xlabel("x 轴", loc="left")
764
+ """
765
+ family, size, color = _font_props(fontdict, kwargs)
766
+ return _rsplotlib.xlabel(text, color, size, family, loc)
767
+
768
+
769
+ def ylabel(text, fontdict=None, loc=None, **kwargs):
770
+ """设置 y 轴标签文本,并可通过 fontdict / 关键字参数自定义字体属性。
771
+
772
+ 支持的字体属性同 xlabel(family / size / color,关键字参数优先于 fontdict)。
773
+
774
+ loc: 标签垂直位置,可选 'bottom'、'center'、'top',默认 'center'。
775
+
776
+ 用法:
777
+ plt.ylabel("y - label")
778
+ plt.ylabel("y 轴", fontdict={"family": "STHeiti Light", "size": 16, "color": "g"})
779
+ plt.ylabel("y 轴", loc="top")
780
+ """
781
+ family, size, color = _font_props(fontdict, kwargs)
782
+ return _rsplotlib.ylabel(text, color, size, family, loc)
783
+
784
+
785
+ def title(label, fontdict=None, loc=None, **kwargs):
786
+ """设置图表标题文本,并可通过 fontdict / 关键字参数自定义字体属性。
787
+
788
+ 支持的字体属性 (fontdict 的键或直接关键字参数, 关键字参数优先):
789
+ family / fontfamily / fontname: 字体族名 (如 'Courier'、'Times New Roman'、
790
+ 'SimHei' 等)
791
+ size / fontsize: 字号 (points)
792
+ color: 文本颜色 (如 'r'、'#ff0000'、'SeaGreen')
793
+
794
+ loc: 标题水平位置,可选 'left'、'center'、'right',默认 'center'。
795
+
796
+ 用法:
797
+ plt.title("标题")
798
+ plt.title("标题", fontdict={"family": "Courier", "size": 18, "color": "red"})
799
+ plt.title("标题", fontsize=18, color='b')
800
+ plt.title("标题", loc="left")
801
+ """
802
+ family, size, color = _font_props(fontdict, kwargs)
803
+ # 字体族名的解析与注册由 Rust 层的 set_title 统一处理。
804
+ return _rsplotlib.title(label, color, size, family, loc)
805
+
806
+
807
+ def grid(visible=True, **kwargs):
808
+ """显示或隐藏网格线。
809
+
810
+ Args:
811
+ visible: 是否显示 (默认 True)
812
+ color/c: 线颜色
813
+ linestyle/ls: 线型
814
+ linewidth/lw: 线宽
815
+ axis: 坐标轴 ('x', 'y', 或 'both')
816
+ """
817
+ c = kwargs.get('c')
818
+ ls = kwargs.get('linestyle') or kwargs.get('ls')
819
+ lw = kwargs.get('linewidth') or kwargs.get('lw')
820
+ axis = kwargs.get('axis')
821
+ return _rsplotlib.grid(visible, c, ls, lw, axis)
822
+
823
+
824
+ def legend(loc='best', **kwargs):
825
+ """显示图例 (需要 plot 时设置 label 参数)。
826
+
827
+ Args:
828
+ loc: 图例位置 ('best', 'upper right', 'upper left', 'lower left',
829
+ 'lower right', 'upper center', 'lower center',
830
+ 'center left', 'center right', 'center')
831
+ """
832
+ return _rsplotlib.legend(loc)
833
+
834
+
835
+ def xlim(left=None, right=None, **kwargs):
836
+ """设置 x 轴显示范围。
837
+
838
+ Args:
839
+ left: x 轴最小值
840
+ right: x 轴最大值
841
+ """
842
+ return _rsplotlib.xlim(left, right)
843
+
844
+
845
+ def ylim(bottom=None, top=None, **kwargs):
846
+ """设置 y 轴显示范围。
847
+
848
+ Args:
849
+ bottom: y 轴最小值
850
+ top: y 轴最大值
851
+ """
852
+ return _rsplotlib.ylim(bottom, top)
853
+
854
+
855
+ def xticks(ticks=None, labels=None, **kwargs):
856
+ """设置 x 轴刻度。
857
+
858
+ Args:
859
+ ticks: 刻度位置列表
860
+ labels: 刻度标签列表
861
+ """
862
+ ticks = _to_list(ticks)
863
+ return _rsplotlib.xticks(ticks, labels)
864
+
865
+
866
+ def yticks(ticks=None, labels=None, **kwargs):
867
+ """设置 y 轴刻度。
868
+
869
+ Args:
870
+ ticks: 刻度位置列表
871
+ labels: 刻度标签列表
872
+ """
873
+ ticks = _to_list(ticks)
874
+ return _rsplotlib.yticks(ticks, labels)
875
+
876
+
877
+ def xscale(scale, **kwargs):
878
+ """设置 x 轴刻度类型 ('linear', 'log', 'logit', 'symlog' 等)。"""
879
+ return _rsplotlib.xscale(scale)
880
+
881
+
882
+ def yscale(scale, **kwargs):
883
+ """设置 y 轴刻度类型。"""
884
+ return _rsplotlib.yscale(scale)
885
+
886
+
887
+ def margins(x_margin=None, y_margin=None, **kwargs):
888
+ """设置自动缩放的边距。"""
889
+ return _rsplotlib.margins(x_margin, y_margin)
890
+
891
+
892
+ def box(on=None):
893
+ """设置坐标轴边框。"""
894
+ return _rsplotlib.box_(on)
895
+
896
+
897
+ def minorticks_on():
898
+ """显示次要刻度。"""
899
+ return _rsplotlib.minorticks_on()
900
+
901
+
902
+ def minorticks_off():
903
+ """隐藏次要刻度。"""
904
+ return _rsplotlib.minorticks_off()
905
+
906
+
907
+ # ==================== 子图与布局 ====================
908
+
909
+ def subplots(nrows=1, ncols=1, figsize=None, dpi=None, **kwargs):
910
+ """创建子图网格 (Figure + Axes)。
911
+
912
+ 用法:
913
+ fig, ax = plt.subplots() # 单图 (1x1)
914
+ fig, axes = plt.subplots(2, 2) # 2x2 网格
915
+ fig, axes = plt.subplots(1, 2, figsize=(10, 5)) # 自定义尺寸
916
+
917
+ Args:
918
+ nrows: 子图行数 (默认 1)
919
+ ncols: 子图列数 (默认 1)
920
+ figsize: 图的尺寸 (width, height), 单位英寸
921
+ dpi: 分辨率 (每英寸点数)
922
+ **kwargs: 其他关键字参数
923
+
924
+ Returns:
925
+ (Figure, Axes) 或 (Figure, list[Axes]) 元组
926
+ """
927
+ result = _rsplotlib.subplots(nrows, ncols, figsize, dpi)
928
+ if nrows == 1 and ncols == 1:
929
+ return result # (fig, ax)
930
+ fig = result[0]
931
+ flat_axes = list(result[1])
932
+ # 组织为 2D 列表
933
+ axes_2d = []
934
+ for r in range(nrows):
935
+ row = [flat_axes[r * ncols + c] for c in range(ncols)]
936
+ axes_2d.append(row)
937
+ return fig, axes_2d
938
+
939
+
940
+ def subplot(nrows, ncols, index, **kwargs):
941
+ """创建单个子图。"""
942
+ return _rsplotlib.subplot(nrows, ncols, index)
943
+
944
+
945
+ def tight_layout(**kwargs):
946
+ """自动调整子图布局, 避免标签重叠。"""
947
+ return _rsplotlib.tight_layout()
948
+
949
+
950
+ def subplots_adjust(left=None, right=None, bottom=None, top=None, wspace=None, hspace=None):
951
+ """调整子图布局参数"""
952
+ fig = _get_figure()
953
+ if fig is not None:
954
+ fig.subplots_adjust(left, right, bottom, top, wspace, hspace)
955
+
956
+
957
+ def set_size(width, height):
958
+ """设置图形像素尺寸。"""
959
+ return _rsplotlib.set_size(width, height)
960
+
961
+
962
+ def twinx():
963
+ """创建共享 x 轴的双 y 轴。"""
964
+ return _rsplotlib.twinx()
965
+
966
+
967
+ def twiny():
968
+ """创建共享 y 轴的双 x 轴。"""
969
+ return _rsplotlib.twiny()
970
+
971
+
972
+ # ==================== 图形控制 ====================
973
+
974
+ def figure(num=None, figsize=None, dpi=None, **kwargs):
975
+ """创建新的 Figure 对象。
976
+
977
+ Args:
978
+ num: 图形编号 (兼容 matplotlib, 未实际使用)
979
+ figsize: (width, height) 英寸数
980
+ dpi: 分辨率
981
+ **kwargs: 其他关键字参数
982
+
983
+ Returns:
984
+ Figure 对象
985
+ """
986
+ fig = _rsplotlib.figure()
987
+ d = dpi if dpi is not None else DEFAULT_DPI
988
+ fig.set_dpi(d)
989
+ if figsize is not None:
990
+ w_inch, h_inch = figsize
991
+ fig.set_size(round(w_inch * d), round(h_inch * d))
992
+ else:
993
+ w, h = _get_rcparams().get('figure.figsize', list(DEFAULT_FIGSIZE))
994
+ fig.set_size(round(w * d), round(h * d))
995
+ return fig
996
+
997
+
998
+ def savefig(fname, **kwargs):
999
+ """保存图形到文件。
1000
+
1001
+ 支持的文件格式:
1002
+ - .png: 便携式网络图形 (位图)
1003
+ - .jpg: JPEG 图像
1004
+ - .svg: 可缩放矢量图形
1005
+
1006
+ 用法:
1007
+ plt.savefig('figure.png')
1008
+ plt.savefig('figure.png', dpi=300) # 高分辨率
1009
+ plt.savefig('figure.svg')
1010
+
1011
+ Args:
1012
+ fname: 输出文件名 (含扩展名)
1013
+ dpi: 分辨率 (每英寸点数, 默认与创建时一致)。
1014
+ 对于高清晰度出版物, 使用 300 或更高。
1015
+ **kwargs: 其他关键字参数
1016
+ """
1017
+ dpi = kwargs.get('dpi')
1018
+ fig = _get_figure()
1019
+ if fig is not None:
1020
+ if dpi is not None:
1021
+ fig.savefig(fname, dpi)
1022
+ else:
1023
+ fig.savefig(fname, dpi=DEFAULT_DPI)
1024
+ return
1025
+ # 无 Figure 时回退到模块级
1026
+ if dpi is not None:
1027
+ _rsplotlib.savefig(fname, dpi)
1028
+ else:
1029
+ _rsplotlib.savefig(fname)
1030
+
1031
+
1032
+ def show(**kwargs):
1033
+ """在默认应用中显示图形。"""
1034
+ return _rsplotlib.show()
1035
+
1036
+
1037
+ def gca(**kwargs):
1038
+ """获取当前 Axes。"""
1039
+ return _rsplotlib.gca()
1040
+
1041
+
1042
+ def gcf(**kwargs):
1043
+ """获取当前 Figure。"""
1044
+ return _rsplotlib.gcf()
1045
+
1046
+
1047
+ def cla():
1048
+ """清空当前 Axes 内容。"""
1049
+ return _rsplotlib.cla()
1050
+
1051
+
1052
+ def clf():
1053
+ """清空当前 Figure 内容 (清除所有子图)。"""
1054
+ return _rsplotlib.clf()
1055
+
1056
+
1057
+ def close(fig=None):
1058
+ """关闭当前 Figure。
1059
+
1060
+ Args:
1061
+ fig: 图形或 'all' (兼容 matplotlib)
1062
+ """
1063
+ return _rsplotlib.close()
1064
+
1065
+
1066
+ def axis(arg=None, **kwargs):
1067
+ """坐标轴控制: axis('off') 隐藏, axis('equal') 等比例。"""
1068
+ if arg == 'off':
1069
+ try:
1070
+ _rsplotlib.gca()._axis_off()
1071
+ except Exception:
1072
+ pass
1073
+ elif arg in ('equal', 'scaled'):
1074
+ gca().set_aspect('equal')
1075
+ return None
1076
+
1077
+
1078
+ def colorbar(mappable=None, **kwargs):
1079
+ """添加颜色条 (占位实现)。"""
1080
+ pass
1081
+
1082
+
1083
+ def get_cmap(name=None, lut=None):
1084
+ """获取颜色映射 (占位实现)。"""
1085
+ return name
1086
+
1087
+
1088
+ # ==================== Figure 类补丁 ====================
1089
+
1090
+ def _patch_figure_add_subplot():
1091
+ """为 Rust Figure 类添加 add_subplot(nrows, ncols, index) 支持。"""
1092
+ from . import rsplotlib as _rs
1093
+
1094
+ _orig_add_subplot = _rs.Figure.add_subplot
1095
+
1096
+ def _add_subplot(self, *args):
1097
+ if len(args) == 1:
1098
+ return _orig_add_subplot(self, args[0])
1099
+ elif len(args) == 3:
1100
+ nrows, ncols, index = args
1101
+ if isinstance(index, tuple):
1102
+ indices = [i - 1 for i in index]
1103
+ row_start = indices[0] // ncols
1104
+ row_end = indices[-1] // ncols + 1
1105
+ col_start = indices[0] % ncols
1106
+ col_end = indices[-1] % ncols + 1
1107
+ else:
1108
+ index_0 = index - 1
1109
+ row_start = index_0 // ncols
1110
+ row_end = row_start + 1
1111
+ col_start = index_0 % ncols
1112
+ col_end = col_start + 1
1113
+ from .layout.gridspec import SubplotSpec
1114
+ spec = SubplotSpec(None, row_start, row_end, col_start, col_end)
1115
+ return _orig_add_subplot(self, spec)
1116
+ else:
1117
+ raise TypeError(
1118
+ f"add_subplot() takes 1 or 3 positional arguments but {len(args)} were given"
1119
+ )
1120
+
1121
+ _rs.Figure.add_subplot = _add_subplot
1122
+
1123
+
1124
+ def _patch_axes():
1125
+ """为 Rust Axes 类添加 Python 级别的 API 兼容补丁。"""
1126
+ from . import rsplotlib as _rs
1127
+
1128
+ # plot: 支持单参数 ax.plot(y)
1129
+ _orig_plot = _rs.Axes.plot
1130
+
1131
+ def _plot(self, *args, **kwargs):
1132
+ if len(args) == 1:
1133
+ y = args[0]
1134
+ x = list(range(len(y) if hasattr(y, '__len__') else 0))
1135
+ return _orig_plot(self, x, y, **kwargs)
1136
+ return _orig_plot(self, *args, **kwargs)
1137
+
1138
+ _rs.Axes.plot = _plot
1139
+
1140
+ # hlines / vlines: Rust 层已支持批量, 直接转发
1141
+ _orig_hlines = _rs.Axes.hlines
1142
+ _orig_vlines = _rs.Axes.vlines
1143
+
1144
+ def _hlines(self, y, xmin=None, xmax=None, color=None, linestyle=None, linewidth=None, **kwargs):
1145
+ y_arr = _to_list(y) if isinstance(y, (list, tuple)) or hasattr(y, 'tolist') else [y]
1146
+ return _orig_hlines(self, y_arr, color, linestyle, linewidth)
1147
+
1148
+ def _vlines(self, x, ymin=None, ymax=None, color=None, linestyle=None, linewidth=None, **kwargs):
1149
+ x_arr = _to_list(x) if isinstance(x, (list, tuple)) or hasattr(x, 'tolist') else [x]
1150
+ return _orig_vlines(self, x_arr, color, linestyle, linewidth)
1151
+
1152
+ _rs.Axes.hlines = _hlines
1153
+ _rs.Axes.vlines = _vlines
1154
+
1155
+ # scatter: 支持 c/s 为数组
1156
+ _orig_scatter = _rs.Axes.scatter
1157
+
1158
+ def _scatter(self, x, y, s=20.0, c=None, marker='o', label=None, alpha=1.0, **kwargs):
1159
+ color = kwargs.pop('color', None)
1160
+ if c is None and color is not None:
1161
+ c = color
1162
+ is_multi_s = isinstance(s, (list, tuple))
1163
+ is_multi_c = isinstance(c, (list, tuple)) and c and isinstance(c[0], str)
1164
+ if is_multi_s or is_multi_c:
1165
+ return self.scatter_multi(x, y, s if is_multi_s else None, c if is_multi_c else None, marker, label, alpha)
1166
+ return _orig_scatter(self, x, y, s, c, marker, label, alpha)
1167
+
1168
+ _rs.Axes.scatter = _scatter
1169
+
1170
+ # set_xlim / set_ylim: 支持元组参数 (left, right)
1171
+ _orig_set_xlim = _rs.Axes.set_xlim
1172
+ _orig_set_ylim = _rs.Axes.set_ylim
1173
+
1174
+ def _set_xlim(self, *args, **kwargs):
1175
+ if len(args) == 1 and isinstance(args[0], (tuple, list)):
1176
+ lo, hi = args[0]
1177
+ return _orig_set_xlim(self, left=lo, right=hi)
1178
+ return _orig_set_xlim(self, *args, **kwargs)
1179
+
1180
+ def _set_ylim(self, *args, **kwargs):
1181
+ if len(args) == 1 and isinstance(args[0], (tuple, list)):
1182
+ lo, hi = args[0]
1183
+ return _orig_set_ylim(self, bottom=lo, top=hi)
1184
+ return _orig_set_ylim(self, *args, **kwargs)
1185
+
1186
+ _rs.Axes.set_xlim = _set_xlim
1187
+ _rs.Axes.set_ylim = _set_ylim
1188
+
1189
+
1190
+ _patch_figure_add_subplot()
1191
+ _patch_axes()
1192
+
1193
+
1194
+ style = _style_module.style