1. 为什么数据可视化绕不开Pyecharts
做Web开发或者数据分析的朋友,大概率都经历过这样一个阶段:辛辛苦苦从数据库里把数据捞出来,用Pandas清洗完,最后要展示给别人的时候,发现Matplotlib画出来的图放在网页上怎么看怎么别扭。静态图片、不能交互、样式老旧,放在后台管理系统里就像西装上打了个补丁。我早期做数据看板的时候也踩过这个坑,后来接触到Pyecharts,才算真正把“数据”和“Web展示”这两件事串起来了。
Pyecharts这个库,说白了就是ECharts的Python版本。ECharts是百度开源的一个前端可视化库,基于JavaScript,功能非常强大,图表类型丰富,交互体验也好。但问题在于,做数据分析的人往往不擅长写JS,而前端工程师又不一定懂数据处理的逻辑。Pyecharts的出现就是为了解决这个断层——你用Python写好数据处理逻辑,直接调用Pyecharts生成图表配置,最终输出一个HTML文件或者嵌入到Web框架里,整个过程不需要你写一行JavaScript。
这篇文章适合谁看?如果你是刚接触数据可视化的Python开发者,或者正在做后台管理系统需要嵌入图表的前端同学,又或者你已经在用Matplotlib但想升级到交互式图表,那这篇内容应该能帮你省下不少查文档的时间。我会从Pyecharts的核心设计思路讲起,把配置项的底层逻辑拆开,然后给一个完整的实战案例,最后把我自己踩过的坑和排查经验整理出来。不夸张地说,看完你就能直接上手干活。
2. Pyecharts整体设计与核心思路拆解
2.1 Pyecharts和ECharts的关系到底是什么
很多人第一次用Pyecharts的时候会困惑:我到底是在写Python还是在写JS?其实理解了这个库的设计哲学,这个问题就迎刃而解了。
ECharts的核心是一套JSON配置。你在ECharts官网看到的那些示例,本质上都是在描述一个option对象,这个对象决定了图表长什么样、数据是什么、交互怎么响应。Pyecharts做的事情,就是用Python的类和方法,帮你把这个JSON配置对象“拼”出来。你调用Bar()创建一个柱状图对象,调用add_xaxis()和add_yaxis()添加数据,这些操作最终都会被翻译成ECharts能识别的配置项。
所以你在Pyecharts里看到的很多参数名,比如xaxis_opts、yaxis_opts、tooltip_opts,其实都是ECharts原生配置项的Python化封装。理解了这一层,你就知道遇到问题该去哪里找答案了——Pyecharts文档没写清楚的,直接去翻ECharts的官方配置手册,大概率能找到对应的说明。
这种设计带来的好处是显而易见的。你不需要学习JavaScript语法,不需要理解前端构建工具,甚至不需要知道HTML和CSS的细节,就能生成一个功能完整的交互式图表。但代价也有,就是当你想做一些深度定制的时候,还是得回到ECharts的配置体系里去理解那些参数的真正含义。
2.2 为什么选择Pyecharts而不是Matplotlib或Plotly
这个问题我被问过很多次,我的回答通常是:看你的使用场景。
Matplotlib是Python可视化的老大哥,功能全面,适合做科学计算和静态图表的输出。但它的设计初衷是替代MATLAB的绘图功能,所以在Web交互方面先天不足。你当然可以用Matplotlib生成图片再嵌入网页,但那就失去了交互性,用户不能缩放、不能悬停查看数值、不能筛选数据系列。
Plotly在交互性上做得很好,而且也有Python版本。但Plotly的默认渲染方式依赖于它的云服务或者需要额外的JS文件,在国内网络环境下有时候会遇到加载问题。另外Plotly的图表风格偏学术,放在商业后台系统里需要做不少样式调整。
Pyecharts的优势在于:第一,它生成的HTML文件是完全自包含的,所有依赖都在本地,不依赖外部CDN;第二,它的图表风格默认就很“商业”,适合直接放在管理后台;第三,它对中文支持非常好,不需要额外配置字体;第四,它的链式调用写法很符合Python的使用习惯,代码读起来很顺畅。
当然Pyecharts也不是没有缺点。它的文档更新速度跟不上ECharts的版本迭代,有些新特性可能没有及时封装。另外它的自定义能力虽然强,但需要你对ECharts的配置体系有一定了解。不过总体来说,对于Web后台的数据展示场景,Pyecharts是我目前最推荐的方案。
2.3 渲染机制:从Python对象到浏览器图表的完整链路
Pyecharts的渲染流程可以分为三个阶段,理解这个过程对你排查问题很有帮助。
第一个阶段是配置构建。你在Python里创建图表对象、添加数据、设置配置项,这些操作在内存中构建了一个完整的配置字典。这个字典的结构和ECharts的option对象是一一对应的。
第二个阶段是模板渲染。Pyecharts内置了一套Jinja2模板,会把上一步生成的配置字典嵌入到HTML模板中。这个模板里包含了ECharts的JS文件引用、初始化代码、以及你设置的图表配置。最终生成一个完整的HTML字符串。
第三个阶段是输出。你可以选择把HTML字符串保存为文件,也可以直接在Jupyter Notebook里渲染,还可以通过Flask、Django等Web框架返回给浏览器。不同的输出方式对应不同的渲染方法,比如render()生成HTML文件,render_notebook()在Notebook里显示,render_embed()返回HTML字符串。
这里有一个关键点:Pyecharts生成的HTML文件默认会引用在线的ECharts JS文件。如果你部署的环境不能访问外网,图表就加载不出来。解决办法是在初始化图表时指定js_host参数,或者使用render_embed()方法把JS代码直接嵌入HTML。这个坑我在内网部署的时候踩过,排查了半天才发现是JS文件加载失败。
3. 核心配置项与实操要点解析
3.1 图表初始化的关键参数怎么选
创建一个Pyecharts图表对象是所有操作的起点。以柱状图为例,最基本的初始化方式是Bar(),但这里面其实有不少可以配置的参数。
init_opts是最重要的一个参数,它接收一个InitOpts对象,用来控制图表的初始化行为。其中width和height控制图表容器的尺寸,默认是900px宽、500px高。如果你要嵌入到固定宽度的后台面板里,记得把这个值改成百分比或者具体的像素值,否则会出现滚动条。
page_title参数控制HTML文件的标题,也就是浏览器标签页上显示的文字。这个参数在生成多个图表的时候特别有用,方便你在浏览器里区分不同的页面。
theme参数可以设置图表主题,Pyecharts内置了十几种主题,比如LIGHT、DARK、CHALK、ESSOS等。如果你要做暗色系的后台系统,直接用DARK主题能省不少样式调整的时间。
bg_color参数设置图表背景色,这个在嵌入到已有页面的时候很有用,可以设置成透明或者和页面背景一致的颜色。
还有一个容易被忽略的参数是js_host。前面提到过,Pyecharts默认从在线地址加载ECharts的JS文件。如果你在内网环境部署,需要把这个参数改成你本地存放JS文件的路径。具体做法是下载ECharts的JS文件放到静态资源目录,然后设置js_host为对应的URL。
3.2 数据添加的两种方式:add_xaxis和add_yaxis
Pyecharts的数据添加逻辑很直观:add_xaxis()添加X轴数据,add_yaxis()添加Y轴数据。但这里面有几个细节值得注意。
add_xaxis()接收一个列表,列表里的每个元素对应X轴上的一个刻度。这个列表的长度决定了图表的数据点数量。如果你后续添加的Y轴数据长度和X轴不一致,图表会报错或者显示异常。所以我的习惯是先把X轴数据准备好,再根据X轴的长度来组织Y轴数据。
add_yaxis()的第一个参数是系列名称,这个名称会显示在图例和提示框里。第二个参数是数据列表。除此之外,还有很多可选参数,比如color设置系列颜色,stack设置堆叠,is_selected控制默认是否选中。
这里有一个实战经验:当你需要添加多个数据系列的时候,多次调用add_yaxis()就行了。但要注意,每次调用都会生成一个系列,系列名称不能重复,否则图例会出问题。另外,如果你想让某个系列默认不显示,可以把is_selected设为False,用户可以通过点击图例来切换显示状态。
对于折线图来说,还有一个重要的参数是is_smooth,设置为True可以让折线变成平滑曲线。这个在展示趋势数据的时候视觉效果更好,但要注意平滑曲线可能会让数据的精确性看起来打折扣,所以如果是需要精确读数的场景,还是用默认的直线比较好。
3.3 全局配置项set_global_opts的实战用法
set_global_opts()是Pyecharts里最强大的方法之一,它控制图表的标题、图例、提示框、工具箱、坐标轴等全局配置。这个方法的参数很多,但常用的就那么几个,我挑重点说。
标题配置用title_opts,接收一个TitleOpts对象。你可以设置主标题、副标题、标题位置、标题样式等。这里有个小技巧:如果标题太长,可以设置title_link让标题变成一个可点击的链接,点击后跳转到详情页面。
图例配置用legend_opts,接收一个LegendOpts对象。你可以控制图例的位置、排列方向、是否可滚动等。当系列比较多的时候,建议把图例设置为可滚动,否则图例会占掉太多空间。
提示框配置用tooltip_opts,这个非常重要。默认情况下,鼠标悬停在数据点上会显示数值。你可以通过TooltipOpts来定制提示框的内容和样式。比如设置trigger为axis可以让提示框显示整条轴上的所有系列数据,这在对比分析的时候很有用。
工具箱配置用toolbox_opts,可以在图表右上角添加一些实用工具,比如下载图片、数据视图、缩放等。data_view工具特别实用,用户点击后可以直接看到图表背后的数据表格,方便核对数据。
坐标轴配置用xaxis_opts和yaxis_opts。这里最常用的参数是name设置轴名称,type_设置轴类型(category或value),axislabel_opts设置轴标签的旋转角度。当X轴标签比较长的时候,设置rotate为45度可以避免标签重叠。
3.4 系列配置项set_series_opts的细节把控
set_series_opts()用来配置数据系列的样式,比如标签、标记点、标记线等。
标签配置用label_opts,控制数据点上是否显示数值以及显示的位置和格式。默认情况下,柱状图和折线图的数据点上不显示数值,需要手动开启。设置LabelOpts(is_show=True)可以让数值显示出来,position参数控制显示位置,比如top、inside、right等。
标记点配置用markpoint_opts,可以在图表上标记最大值、最小值、平均值等。这个在数据分析场景下很实用,用户一眼就能看到关键数据点。
标记线配置用markline_opts,可以添加平均线、趋势线等参考线。比如在销售数据图表上添加一条平均线,可以直观地看出哪些月份高于平均、哪些低于平均。
这里有一个容易踩的坑:set_series_opts()是对所有系列生效的。如果你只想对某个系列设置标签,需要在add_yaxis()的时候通过label_opts参数单独设置。这个细节在官方文档里没有特别强调,但实际使用中经常会遇到。
4. 完整实战案例:从数据到交互式图表的全流程
4.1 场景设定与数据准备
假设我们要为一个电商后台系统做一个销售数据看板,需要展示最近12个月的销售额和订单量趋势,同时用柱状图对比各品类的销售情况。数据来源是数据库,但我们这里用模拟数据来演示。
先准备数据。销售额和订单量用两个列表表示,月份作为X轴数据。品类数据用一个字典来组织,键是品类名称,值是对应的销售额。
import random from pyecharts import options as opts from pyecharts.charts import Bar, Line, Grid # 模拟月份数据 months = [f"{i}月" for i in range(1, 13)] # 模拟销售额数据(单位:万元) sales_amount = [round(random.uniform(80, 200), 1) for _ in range(12)] # 模拟订单量数据(单位:百单) order_count = [round(random.uniform(30, 80), 1) for _ in range(12)] # 模拟品类销售数据 categories = ["数码", "服饰", "食品", "家居", "美妆"] category_sales = [round(random.uniform(50, 150), 1) for _ in range(5)]数据准备的原则是:X轴数据长度要和Y轴数据长度一致,数值类型要统一。如果是真实场景,建议先用Pandas做数据清洗和聚合,确保没有空值和异常值。
4.2 构建组合图表:柱状图与折线图的叠加
Pyecharts支持通过overlap()方法把多个图表叠加在一起。我们先把柱状图和折线图分别创建好,然后叠加。
# 创建柱状图 bar = ( Bar() .add_xaxis(months) .add_yaxis("销售额(万元)", sales_amount, color="#5470c6") .set_global_opts( title_opts=opts.TitleOpts(title="月度销售趋势"), tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"), legend_opts=opts.LegendOpts(pos_top="5%"), ) ) # 创建折线图 line = ( Line() .add_xaxis(months) .add_yaxis("订单量(百单)", order_count, color="#91cc75", is_smooth=True) .set_series_opts( label_opts=opts.LabelOpts(is_show=False), markpoint_opts=opts.MarkPointOpts( data=[ opts.MarkPointItem(type_="max", name="最大值"), opts.MarkPointItem(type_="min", name="最小值"), ] ), ) ) # 叠加图表 bar.overlap(line)这里有几个关键点。第一,tooltip_opts的trigger设置为axis,这样鼠标悬停时会同时显示柱状图和折线图的数据,方便对比。第二,折线图设置了is_smooth=True,让趋势线更平滑。第三,折线图添加了最大值和最小值的标记点,方便快速定位关键数据。
4.3 添加品类对比柱状图并组合布局
接下来创建品类对比的柱状图,然后用Grid组件把两个图表组合在一起。
# 创建品类对比柱状图 category_bar = ( Bar() .add_xaxis(categories) .add_yaxis("品类销售额(万元)", category_sales, color="#fac858") .set_global_opts( title_opts=opts.TitleOpts(title="品类销售对比", pos_top="55%"), xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=0)), legend_opts=opts.LegendOpts(is_show=False), ) ) # 使用Grid组合布局 grid = ( Grid(init_opts=opts.InitOpts(width="100%", height="800px")) .add(bar, grid_opts=opts.GridOpts(pos_bottom="60%")) .add(category_bar, grid_opts=opts.GridOpts(pos_top="60%")) ) # 生成HTML文件 grid.render("sales_dashboard.html")Grid组件的用法很直观:通过add()方法添加图表,通过grid_opts控制每个图表在页面上的位置。pos_bottom和pos_top参数用百分比来控制,这样在不同屏幕尺寸下都能保持相对布局。
这里有一个实操心得:Grid布局的坐标系统是相对于整个画布的,所以第一个图表的pos_bottom和第二个图表的pos_top要配合好,避免重叠。我一般会留出10%左右的间距,视觉效果比较舒服。
4.4 渲染输出与Web框架集成
生成HTML文件只是第一步,实际项目中我们通常需要把图表嵌入到Web框架里。以Flask为例,有两种常见的集成方式。
第一种方式是生成HTML文件,然后通过Flask的静态文件服务返回。这种方式适合图表不经常变化的场景,可以提前生成好HTML文件,减少服务器压力。
第二种方式是在视图函数里动态生成图表配置,然后通过render_embed()方法获取HTML字符串,嵌入到模板中返回。这种方式适合数据实时变化的场景。
from flask import Flask, render_template from pyecharts.charts import Bar from pyecharts import options as opts app = Flask(__name__) @app.route("/dashboard") def dashboard(): bar = ( Bar() .add_xaxis(["A", "B", "C", "D"]) .add_yaxis("销量", [100, 200, 150, 300]) .set_global_opts(title_opts=opts.TitleOpts(title="实时销量")) ) # 获取图表配置的JSON chart_json = bar.dump_options_with_quotes() return render_template("dashboard.html", chart_json=chart_json)在模板中,你需要引入ECharts的JS文件,然后用获取到的配置初始化图表。这种方式的好处是前后端分离,图表配置由后端生成,前端负责渲染,职责清晰。
注意:使用
dump_options_with_quotes()方法时,返回的JSON字符串已经处理好了引号转义,可以直接嵌入到JS代码中。如果你用的是dump_options(),需要自己处理转义问题。
5. 常见问题与排查技巧实录
5.1 图表不显示或显示空白怎么排查
这是新手遇到最多的问题。图表不显示的原因通常有以下几种。
第一种是JS文件加载失败。打开浏览器的开发者工具,看Console面板有没有报错。如果看到echarts is not defined或者类似的错误,说明ECharts的JS文件没有加载成功。解决办法是检查js_host配置,或者改用render_embed()方法把JS代码内嵌到HTML里。
第二种是数据格式不对。Pyecharts对数据格式有一定要求,X轴和Y轴的数据长度必须一致,数据类型必须是可序列化的。如果你传入的是NumPy数组或者Pandas Series,建议先转换成Python列表。另外,如果数据里有None或NaN,图表也可能显示异常。
第三种是容器尺寸问题。如果图表的父容器没有设置高度,图表可能显示为空白。Pyecharts生成的HTML里,图表容器默认有固定的宽高,但如果你把它嵌入到自己的页面里,需要确保父容器有明确的尺寸。
第四种是配置项冲突。比如你同时设置了title_opts和legend_opts的位置,导致它们重叠在一起。这种情况下图表其实已经渲染了,只是被遮挡了。调整一下位置参数就能解决。
5.2 中文显示乱码或字体不一致的处理
Pyecharts默认使用浏览器自带的字体渲染中文,一般情况下不会出现乱码。但如果你在图表里使用了自定义字体,或者部署环境的系统字体缺失,就可能出现显示异常。
解决办法是在InitOpts里设置font_family参数,指定一个通用的字体族。比如font_family="Microsoft YaHei, SimHei, sans-serif",这样浏览器会按顺序尝试加载字体,确保中文能正常显示。
另外,如果你在Jupyter Notebook里渲染图表,中文显示可能和浏览器里不一致。这是因为Notebook的渲染环境和浏览器不同。解决办法是在Notebook里使用render_notebook()方法,它会生成一个iframe来渲染图表,字体环境更接近浏览器。
5.3 大数据量下的性能优化建议
当数据点超过几千个的时候,图表的渲染速度会明显下降,交互也会变得卡顿。这时候需要做一些优化。
第一,开启数据采样。ECharts支持sampling参数,可以在数据量大的时候自动采样,减少渲染的数据点数量。在Pyecharts里可以通过set_series_opts()设置sampling="lttb"来开启。
第二,关闭动画效果。动画虽然好看,但在大数据量下会消耗大量计算资源。通过InitOpts的animation_opts参数可以关闭动画。
第三,使用dataZoom组件。当数据点很多的时候,用户通常不需要一次性看到所有数据。添加dataZoom组件可以让用户通过滑动条来查看特定范围的数据,既减少了渲染压力,又提升了交互体验。
第四,考虑数据聚合。如果数据点实在太多,建议在Python端先做聚合处理,比如按小时、按天汇总,减少传给前端的数据量。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 图表空白 | JS文件未加载 | 查看Console报错 | 检查js_host或使用render_embed |
| 数据不显示 | 数据格式错误 | 检查数据类型和长度 | 转换为列表,确保XY轴长度一致 |
| 中文乱码 | 字体缺失 | 检查系统字体 | 设置font_family参数 |
| 图表卡顿 | 数据量过大 | 查看数据点数量 | 开启采样、关闭动画、添加dataZoom |
| 图例重叠 | 位置配置冲突 | 检查legend_opts | 调整pos_top或pos_bottom |
| 提示框不显示 | tooltip未配置 | 检查tooltip_opts | 设置trigger为axis或item |
提示:排查问题时,建议先用最简单的配置生成一个图表,确认基础环境没问题,再逐步添加配置项。这样能快速定位是哪个配置项导致的问题。
5.5 几个我踩过的坑和独家经验
第一个坑是add_yaxis()的系列名称重复。有一次我写循环添加多个系列,不小心让两个系列用了同一个名称,结果图例只显示一个,另一个系列的数据虽然渲染了但无法通过图例控制。排查了半天才发现是名称重复。所以建议在循环里用索引或者唯一标识来命名系列。
第二个坑是Grid布局的坐标计算。GridOpts的pos_top、pos_bottom、pos_left、pos_right参数都是相对于整个画布的百分比。如果你添加了多个图表,需要确保它们的区域不重叠。我的经验是先用纸笔画一下布局,算出每个图表应该占的区域,再设置对应的参数。
第三个坑是render()方法的路径问题。render()默认会在当前工作目录生成HTML文件,如果你在Web框架里调用,可能会生成到意想不到的位置。建议始终指定绝对路径,或者使用render_embed()返回字符串。
第四个坑是版本兼容性。Pyecharts的版本更新比较快,不同版本之间的API可能有变化。比如v1和v2的配置项写法就有不少差异。建议在项目开始时就锁定版本,避免中途升级导致代码不兼容。
6. 进阶技巧:让图表更专业更实用
6.1 自定义主题与样式微调
Pyecharts内置的主题虽然够用,但如果你想做出更有品牌感的图表,可以自定义主题。自定义主题的本质是覆盖ECharts的默认配置,你可以通过InitOpts的theme参数传入一个自定义主题的JSON文件路径。
更简单的方式是直接在代码里覆盖配置项。比如修改全局的字体颜色、背景色、网格线样式等。这些配置可以通过set_global_opts()里的各个子配置项来实现。
另外,Pyecharts支持通过JavascriptCode注入自定义的JS代码,实现一些配置项无法完成的效果。比如自定义提示框的HTML内容、添加图表点击事件等。这个功能比较高级,但用好了能实现非常灵活的交互效果。
6.2 图表联动与数据筛选的实现思路
在实际的数据看板里,经常需要多个图表联动。比如点击某个品类的柱状图,其他图表自动筛选出该品类的数据。这个功能在Pyecharts里可以通过JavascriptCode和ECharts的事件机制来实现。
基本思路是:在图表A上绑定点击事件,获取点击的数据项,然后调用图表B的setOption()方法更新数据。这需要你对ECharts的API有一定了解,但实现起来并不复杂。
另一种联动方式是通过dataZoom组件。当多个图表共享同一个dataZoom时,拖动其中一个图表的缩放条,其他图表也会同步缩放。这个在展示时间序列数据的时候特别实用。
6.3 导出与分享:生成可独立运行的HTML报告
Pyecharts生成的HTML文件是完全自包含的,你可以直接发给别人,对方用浏览器打开就能看到交互式图表,不需要安装任何环境。这个特性在给非技术同事做数据汇报的时候特别方便。
如果你需要生成一份包含多个图表的完整报告,可以使用Page组件。Page可以把多个图表组织成一个页面,支持拖拽布局和标签页切换。生成的文件同样是一个独立的HTML,方便分享。
另外,Pyecharts还支持生成图片格式。通过render()方法生成HTML后,可以用浏览器截图或者使用ECharts自带的下载功能导出PNG图片。如果你需要批量生成图片,可以考虑使用Selenium等工具自动化操作。
6.4 与其他Python库的配合使用
Pyecharts虽然强大,但它只负责图表的渲染和展示。在实际项目里,你通常需要和其他库配合使用。
和Pandas配合是最常见的场景。你可以用Pandas做数据清洗和聚合,然后把处理好的数据传给Pyecharts。Pandas的groupby()、pivot_table()等方法能帮你快速整理出图表需要的数据格式。
和Flask/Django配合是Web开发的标准做法。后端负责数据处理和图表配置生成,前端负责页面渲染和交互。这种前后端分离的架构让代码更清晰,也更容易维护。
和Jupyter Notebook配合适合做数据探索和分析。在Notebook里可以边写代码边看图表效果,调整配置项也很方便。分析完成后,把代码整理成脚本或者Web服务,就能直接部署到生产环境。
7. 我个人在实际操作中的体会
Pyecharts这个库,我用了大概有三年多的时间,从最早的v0.5版本到现在的v2版本,算是见证了它的成长。说实话,它不是一个完美的库,文档有时候跟不上版本更新,某些高级配置需要你去翻ECharts的原生文档才能理解。但它的核心价值在于:让Python开发者能够用自己熟悉的语言,快速生成专业级的Web图表。
我最大的体会是,不要试图用Pyecharts去实现所有ECharts的功能。Pyecharts的定位是“够用就好”,它封装了最常用的配置项,让你能快速出活。如果你需要深度定制,直接去写ECharts的配置或者用JS扩展,可能比在Pyecharts里绕来绕去更高效。
另外,我建议在项目初期就把图表的配置项抽离成独立的配置文件或者函数。这样当需求变化的时候,你只需要改配置,不需要动业务逻辑。这个习惯能帮你省下大量的维护时间。
最后分享一个小技巧:Pyecharts的dump_options_with_quotes()方法返回的JSON字符串,可以直接保存到文件里,作为图表配置的备份。下次需要复现同样的图表时,直接读取这个JSON文件,用ECharts的setOption()加载就行了,连Python代码都不用跑。这个在调试和排查问题的时候特别有用。