1. 为什么我选择用 Jupyter Notebook 配合 Tushare 做数据分析入门
做数据分析这几年,我接触过不少刚入门的朋友,他们最常问的一个问题就是:“我想学 Python 数据分析,但不知道从哪里下手,有没有那种能让我马上看到结果、又不会一开始就被环境配置劝退的路子?”每次遇到这个问题,我的回答几乎都是一样的:找一个真实的数据源,配一个能即时反馈的交互环境,从第一行代码开始就让你看到数据长什么样。而 Tushare 加 Jupyter Notebook 这个组合,恰好就是我认为最适合新手跨过第一道门槛的搭配。
Tushare 是一个提供金融数据接口的 Python 库,你可以在代码里直接调用它获取股票行情、财务指标、基金净值、宏观经济等各类结构化数据。对于学数据分析的人来说,它的价值在于:你不需要自己去爬数据、清洗格式、处理缺失值,它已经把数据整理成了规整的 DataFrame 格式,拿来就能用。而 Jupyter Notebook 则是一个基于浏览器的交互式编程环境,你可以一段一段地写代码、一段一段地执行,每一步的结果都直接显示在代码下方,表格、图表、文字说明可以混排在一起。这种“边写边看”的体验,对于理解数据分析的每一个环节至关重要。
这篇文章适合谁看?如果你已经会一点 Python 基础语法,比如知道列表、字典、循环和函数是怎么回事,但还没有真正做过一个完整的数据分析项目,那这篇内容就是为你准备的。如果你已经用过 pandas 但不知道拿什么数据练手,这篇文章同样能给你一条清晰的路径。我会从环境搭建开始,一步步带你走完数据获取、数据清洗、指标计算、可视化呈现的完整流程,中间会穿插我自己踩过的坑和总结出来的技巧,尽量让你少走弯路。
2. 环境搭建与工具选型:为什么是这套组合
2.1 Jupyter Notebook 的安装与启动方式选择
安装 Jupyter Notebook 目前最省心的方式是通过 Anaconda 发行版。Anaconda 把 Python 解释器、Jupyter Notebook、pandas、numpy、matplotlib 这些数据分析常用的库全部打包好了,装完之后不需要再单独配置什么,直接就能用。下载地址在 Anaconda 官网,选择对应操作系统的安装包即可。安装过程中有一个选项叫“Add Anaconda to PATH”,Windows 用户建议勾选,不然后面在命令行里调用 jupyter 命令会麻烦一些。
装好之后,打开终端或命令行,输入:
jupyter notebook回车之后,浏览器会自动打开一个页面,地址通常是http://localhost:8888。你会看到一个文件浏览器界面,这就是 Jupyter 的工作目录。点击右上角的“New”按钮,选择“Python 3”,就会新建一个 Notebook 文件。每个 Notebook 由一个个“单元格”组成,你可以在单元格里写代码,按Shift + Enter执行,结果直接显示在下方。
如果你不想装 Anaconda,也可以用 pip 单独安装:
pip install notebook然后同样用jupyter notebook启动。不过用 pip 装的话,pandas、matplotlib 这些库需要自己一个个装,对新手来说稍微麻烦一点。我个人的建议是,如果你打算长期做数据分析,直接上 Anaconda,省下来的时间可以用来多分析几组数据。
注意:Jupyter Notebook 启动后,不要关闭那个命令行窗口,否则服务会中断。另外,Notebook 文件的后缀是
.ipynb,它和普通的.py文件不一样,里面保存了代码、执行结果和 Markdown 文本,适合做分析记录,但不适合直接当作脚本运行。
2.2 Tushare 的注册与 Token 获取流程
Tushare 的使用需要一个 Token,这个 Token 相当于你的身份凭证,每次调用接口时都要带上。获取流程并不复杂:先到 Tushare 的官方网站注册一个账号,注册时需要填写邮箱和手机号,完成验证后登录。登录之后进入个人主页,在“接口TOKEN”页面就能看到一串由字母和数字组成的字符串,那就是你的 Token。
拿到 Token 之后,在 Jupyter Notebook 里这样初始化:
import tushare as ts ts.set_token('你的token字符串') pro = ts.pro_api()这里解释一下pro_api()是什么。Tushare 有两个版本的接口:旧版的是ts.get_xxx()这种形式,新版的是pro.xxx()这种形式。新版接口的数据质量更好、字段更规范、更新更及时,所以现在一律推荐用pro_api()。初始化之后,你就可以用pro这个对象去调用各种数据接口了。
提示:Token 不要直接写在代码里然后分享出去,相当于把钥匙给别人了。建议把 Token 存在一个单独的配置文件里,或者用环境变量读取。我自己的做法是在项目目录下建一个
config.py,里面写TOKEN = 'xxx',然后在 Notebook 里from config import TOKEN,这样分享 Notebook 的时候不会泄露。
2.3 为什么不用其他数据源而选 Tushare
市面上能获取金融数据的途径不少,但 Tushare 有几个明显的优势。第一,它的接口设计非常统一,所有数据都返回 pandas DataFrame,列名清晰,不需要你做额外的格式转换。第二,它的数据覆盖面广,从股票日线行情到财务报表、从指数成分到宏观经济,基本上做分析常用的数据都能找到。第三,它的社区活跃,文档虽然不算特别详细,但常见问题在网上都能搜到答案。
相比之下,有些数据源返回的是 JSON 或 CSV,你需要自己解析和整理;有些数据源的接口经常变动,今天能用的代码明天就报错。Tushare 在这方面相对稳定,而且它的积分制度虽然对高频调用有限制,但对于学习用途来说,每天几百次的调用额度完全够用。
还有一个很重要的点:Tushare 的数据是“干净”的。什么叫干净?就是它已经帮你处理好了停牌、除权、复权这些容易让人头疼的问题。你拿到的是可以直接用于计算的数据,不需要花大量时间去清洗。对于初学者来说,这一点非常关键,因为如果一开始就陷入数据清洗的泥潭,很容易失去学习的信心。
3. 数据获取实操:从第一行代码到拿到完整数据集
3.1 获取股票列表与基础信息
做任何分析的第一步,都是先搞清楚你要分析的对象有哪些。对于股票分析来说,第一步就是获取股票列表。用 Tushare 的stock_basic接口可以拿到所有上市公司的基本信息:
# 获取股票列表 stock_list = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date') print(stock_list.shape) stock_list.head()这段代码里,exchange=''表示不限制交易所,list_status='L'表示只获取上市状态的股票,fields参数指定你需要的字段。返回的 DataFrame 包含股票代码、名称、所在地区、所属行业和上市日期。你可以看到,一行代码就拿到了几千条数据,这就是 Tushare 的便利之处。
我通常会先看一下数据的形状和前几行,确认数据是否正常返回。stock_list.shape会告诉你有多少行多少列,head()显示前五行。如果返回的是空 DataFrame,那多半是 Token 有问题或者积分不够。
实操心得:
stock_basic接口返回的ts_code是带交易所后缀的代码,比如000001.SZ表示深交所的平安银行,600000.SH表示上交所的浦发银行。后面调用行情接口时,传入的代码必须是这种带后缀的格式,不能只写000001,否则会报错。
3.2 获取日线行情数据与参数设置
拿到股票列表之后,下一步就是获取具体的行情数据。最常用的是daily接口,可以获取指定股票在指定日期范围内的日线行情:
# 获取单只股票的日线行情 df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231') print(df.shape) df.head()返回的字段包括开盘价、最高价、最低价、收盘价、成交量、成交额等。默认是按日期倒序排列的,也就是最新的数据在最前面。如果你想要按日期正序排列,可以用df.sort_values('trade_date')重新排序。
这里有几个参数细节值得注意。start_date和end_date的格式是YYYYMMDD,必须是字符串,不能写成2023-01-01这种带横线的格式。另外,如果你不指定日期范围,接口会返回最近一个交易日的数据。如果你指定了日期范围但中间有停牌日,那些日期不会出现在结果里,这是正常的。
注意:
daily接口每次最多返回 6000 条数据。如果你要获取一只股票十年的日线数据,大概有 2400 多条,一次就能拿完。但如果你要获取多只股票的数据,就需要分批调用或者用循环。我一般会写一个函数来封装这个逻辑,后面会讲到。
3.3 批量获取多只股票数据的循环写法
单只股票的数据获取很简单,但实际分析中往往需要多只股票的数据。这时候就需要写循环来批量获取。下面是我常用的一个写法:
import time import pandas as pd def get_multi_stock_daily(ts_codes, start_date, end_date): all_data = [] for code in ts_codes: try: df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date) if df is not None and not df.empty: all_data.append(df) time.sleep(0.3) # 控制调用频率 except Exception as e: print(f'{code} 获取失败: {e}') if all_data: return pd.concat(all_data, ignore_index=True) else: return pd.DataFrame() # 示例:获取三只银行股的日线数据 codes = ['000001.SZ', '600000.SH', '601398.SH'] result = get_multi_stock_daily(codes, '20230101', '20231231') print(result.shape)这段代码里有几个关键点。第一,用try...except包裹每次调用,防止某一只股票获取失败导致整个程序中断。第二,用time.sleep(0.3)控制调用频率,避免触发接口的频率限制。第三,用pd.concat把所有 DataFrame 合并成一个大的 DataFrame,方便后续统一处理。
实操心得:
time.sleep的秒数可以根据你的积分等级调整。积分越高,允许的调用频率越高。如果你发现频繁报“调用频率超限”的错误,就把 sleep 的时间调长一点。我一般用 0.3 秒,一天获取几百只股票的数据没有问题。
3.4 数据字段解读与常见返回格式说明
Tushare 返回的 DataFrame 字段命名比较规范,但有些字段的含义需要解释一下。以daily接口为例:
| 字段名 | 含义 | 单位 |
|---|---|---|
| ts_code | 股票代码 | - |
| trade_date | 交易日期 | YYYYMMDD |
| open | 开盘价 | 元 |
| high | 最高价 | 元 |
| low | 最低价 | 元 |
| close | 收盘价 | 元 |
| pre_close | 昨收价 | 元 |
| change | 涨跌额 | 元 |
| pct_chg | 涨跌幅 | % |
| vol | 成交量 | 手 |
| amount | 成交额 | 千元 |
这里特别要注意的是vol的单位是“手”,一手等于 100 股。amount的单位是“千元”,不是“元”。如果你要做成交额的分析,记得把单位换算统一。另外,pct_chg是百分比数值,比如 2.5 表示涨了 2.5%,不是 0.025。
还有一个容易忽略的点:trade_date是字符串格式,不是日期类型。如果你要做时间序列分析,需要先转换:
df['trade_date'] = pd.to_datetime(df['trade_date']) df = df.sort_values('trade_date').reset_index(drop=True)转换之后,你就可以用 pandas 的时间序列功能了,比如按月重采样、计算滚动平均等。
4. 数据清洗与指标计算:让原始数据变成可用信息
4.1 缺失值与异常值的处理思路
Tushare 的数据质量整体不错,但并不意味着完全没有缺失值和异常值。常见的情况包括:停牌期间没有行情数据、某些财务指标在特定报告期没有披露、新股上市初期的数据不完整等。处理这些问题的方法取决于你的分析目标。
如果缺失值是因为停牌造成的,那这些缺失是有意义的,不应该随意填充。你可以选择在分析时排除这些日期,或者用前一个交易日的收盘价填充。如果缺失值是因为数据源本身的问题,比如某个字段在早期版本中没有记录,那可以考虑删除这些字段或者用行业均值填充。
异常值的检测我一般用两种方法。一种是看描述性统计,用df.describe()查看最大值、最小值、均值、标准差,如果某个值明显偏离正常范围,就值得怀疑。另一种是画箱线图,直观地看到离群点。比如涨跌幅超过 10% 的数据(非科创板),在正常交易日是不应该出现的,如果出现了,可能是数据错误或者除权除息导致的。
# 查看描述性统计 print(df[['open', 'high', 'low', 'close', 'pct_chg']].describe()) # 筛选涨跌幅绝对值超过10%的记录 abnormal = df[df['pct_chg'].abs() > 10] print(abnormal[['trade_date', 'close', 'pct_chg']])注意:不要一看到异常值就删掉。有些异常值是真实的市场行为,比如涨跌停、除权除息、重大公告后的剧烈波动。你需要结合具体背景判断,而不是机械地套用规则。
4.2 计算移动平均线与收益率
移动平均线是最基础的技术指标之一,计算起来也很简单。pandas 的rolling()方法可以轻松实现:
# 计算5日、10日、20日移动平均线 df['ma5'] = df['close'].rolling(window=5).mean() df['ma10'] = df['close'].rolling(window=10).mean() df['ma20'] = df['close'].rolling(window=20).mean() # 计算日收益率 df['daily_return'] = df['close'].pct_change() # 计算累计收益率 df['cum_return'] = (1 + df['daily_return']).cumprod() - 1这里解释一下pct_change()的逻辑:它计算的是当前值相对于前一个值的百分比变化。比如收盘价从 10 元涨到 10.5 元,pct_change()返回 0.05,也就是 5%。cumprod()是累积乘积,用来计算从起始日期到当前日期的累计收益。
移动平均线的窗口选择是有讲究的。5 日线反映短期趋势,10 日线反映中期趋势,20 日线反映中长期趋势。如果你做的是短线分析,可能还会用到 3 日线或 7 日线。窗口越小,均线越贴近价格,但噪音也越大;窗口越大,均线越平滑,但滞后性越强。这个取舍需要根据你的分析周期来定。
实操心得:计算移动平均线时,前 N-1 个数据会是 NaN,因为数据不够计算窗口。这是正常的,不要用 0 填充,否则会扭曲均线的形状。如果你要画图,matplotlib 会自动跳过 NaN 值,不会影响视觉效果。
4.3 用 groupby 做分组统计
当你有多只股票的数据时,groupby是你最好的朋友。比如你想计算每只股票的平均涨跌幅、最大回撤、波动率等指标:
# 按股票代码分组计算统计指标 stats = result.groupby('ts_code').agg({ 'close': ['mean', 'std', 'min', 'max'], 'pct_chg': ['mean', 'std'], 'vol': 'sum' }).round(2) print(stats)这段代码会返回一个多层索引的 DataFrame,每只股票一行,包含收盘价的均值、标准差、最小值、最大值,涨跌幅的均值和标准差,以及成交量的总和。你可以用stats.columns查看列名,然后用stats['close']['mean']这种方式访问具体的列。
如果你觉得多层索引不好用,可以在agg之后用stats.columns = ['_'.join(col) for col in stats.columns]把列名扁平化,变成close_mean、close_std这种形式,后续处理会更方便。
4.4 数据合并与透视表的应用
有时候你需要把不同来源的数据合并到一起。比如你有一个股票列表的 DataFrame,还有一个行情数据的 DataFrame,你想把行业信息加到行情数据里:
# 合并股票列表和行情数据 merged = pd.merge(result, stock_list[['ts_code', 'name', 'industry']], on='ts_code', how='left') print(merged.head())pd.merge的how参数控制合并方式:left表示保留左表的所有行,right表示保留右表的所有行,inner表示只保留两表都有的行,outer表示保留所有行。大多数情况下用left就够了。
透视表是另一个很实用的工具。比如你想看每个行业在每个月的平均涨跌幅:
# 先提取月份 merged['month'] = pd.to_datetime(merged['trade_date']).dt.to_period('M') # 创建透视表 pivot = merged.pivot_table(values='pct_chg', index='industry', columns='month', aggfunc='mean') print(pivot.round(2))透视表的好处是一目了然,行是行业,列是月份,单元格是平均涨跌幅。你可以很快看出哪些行业在哪个月表现好,哪些表现差。这种表格在 Jupyter Notebook 里显示得很直观,适合做初步的探索性分析。
5. 可视化呈现:让数据开口说话
5.1 用 matplotlib 画收盘价走势图
数字表格看多了容易疲劳,图表才是数据分析的利器。matplotlib 是 Python 最基础的绘图库,虽然样式不算特别美观,但胜在灵活可控。下面是一个画收盘价走势图的例子:
import matplotlib.pyplot as plt import matplotlib.dates as mdates # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 准备数据 df_plot = df.sort_values('trade_date').copy() df_plot['trade_date'] = pd.to_datetime(df_plot['trade_date']) # 绘图 fig, ax = plt.subplots(figsize=(12, 5)) ax.plot(df_plot['trade_date'], df_plot['close'], label='收盘价', color='#1f77b4', linewidth=1.5) ax.plot(df_plot['trade_date'], df_plot['ma5'], label='MA5', color='#ff7f0e', linewidth=1, linestyle='--') ax.plot(df_plot['trade_date'], df_plot['ma20'], label='MA20', color='#2ca02c', linewidth=1, linestyle='--') ax.set_title('收盘价与移动平均线走势', fontsize=14) ax.set_xlabel('日期') ax.set_ylabel('价格(元)') ax.legend() ax.grid(True, alpha=0.3) # 格式化日期轴 ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m')) ax.xaxis.set_major_locator(mdates.MonthLocator(interval=2)) plt.xticks(rotation=45) plt.tight_layout() plt.show()这段代码里有几个细节值得说明。第一,中文字体设置是必须的,否则标题和标签里的中文会显示成方块。Windows 系统用SimHei,Mac 系统可以用Arial Unicode MS。第二,figsize控制图片大小,宽 12 高 5 是我常用的比例,适合在 Notebook 里显示。第三,alpha=0.3让网格线半透明,不会喧宾夺主。第四,日期轴的格式化用mdates模块,可以控制显示哪些刻度、用什么格式。
实操心得:如果你的数据点很多,比如超过 500 个交易日,建议把
linewidth调小到 1 以下,否则线条会糊在一起。另外,plt.tight_layout()可以自动调整子图间距,避免标签被截断,建议每次都加上。
5.2 用 seaborn 做统计分布图
seaborn 是基于 matplotlib 的高级绘图库,默认样式更好看,而且做统计图表特别方便。比如你想看日收益率的分布:
import seaborn as sns # 设置样式 sns.set_style('whitegrid') sns.set_palette('muted') # 绘制收益率分布图 fig, axes = plt.subplots(1, 2, figsize=(14, 5)) # 左图:直方图 + 核密度曲线 sns.histplot(df['daily_return'].dropna(), bins=50, kde=True, ax=axes[0], color='#4c72b0') axes[0].set_title('日收益率分布', fontsize=13) axes[0].set_xlabel('日收益率') # 右图:箱线图 sns.boxplot(y=df['daily_return'].dropna(), ax=axes[1], color='#dd8452') axes[1].set_title('日收益率箱线图', fontsize=13) axes[1].set_ylabel('日收益率') plt.tight_layout() plt.show()直方图看的是整体分布形状,是正态分布还是偏态分布,有没有明显的双峰。核密度曲线是直方图的平滑版本,更容易看出分布的特征。箱线图看的是中位数、四分位数和离群点,适合快速判断数据的集中趋势和离散程度。
从日收益率的分布中,你通常会发现几个特征:均值接近 0,因为长期来看涨跌大致平衡;分布比正态分布更“尖峰厚尾”,也就是极端值出现的概率比正态分布预测的要高;可能有轻微的偏态,取决于分析的时间段。
5.3 多股票对比图的绘制技巧
当你有多只股票的数据时,对比图能帮你快速看出差异。下面是一个归一化对比的写法:
# 归一化处理:以第一天为基准 pivot_close = merged.pivot_table(values='close', index='trade_date', columns='name', aggfunc='first') normalized = pivot_close / pivot_close.iloc[0] * 100 # 绘图 fig, ax = plt.subplots(figsize=(12, 6)) for col in normalized.columns: ax.plot(pd.to_datetime(normalized.index), normalized[col], label=col, linewidth=1.5) ax.set_title('多股票归一化走势对比(基准=100)', fontsize=14) ax.set_xlabel('日期') ax.set_ylabel('归一化价格') ax.legend(loc='upper left') ax.grid(True, alpha=0.3) plt.xticks(rotation=45) plt.tight_layout() plt.show()归一化的目的是消除价格绝对值的影响。比如一只股票 10 元,另一只 100 元,直接画在一起的话,10 元的那只看起来像一条直线。归一化之后,两只股票都从 100 开始,涨跌幅的差异就一目了然了。
注意:
pivot_table的aggfunc='first'是因为同一天同一只股票只有一条记录,用first取第一条即可。如果你的数据里有重复记录,需要先做去重处理。
5.4 在 Notebook 中嵌入图表的最佳实践
Jupyter Notebook 里显示图表有两种方式。一种是%matplotlib inline,图表直接嵌入在 Notebook 页面里,适合做分析记录。另一种是%matplotlib notebook或%matplotlib widget,图表是交互式的,可以缩放、平移,适合探索数据。我一般用 inline 模式,因为稳定、兼容性好,而且分享 Notebook 的时候别人也能看到图。
如果你想让图表更清晰,可以在绘图前设置 DPI:
%matplotlib inline %config InlineBackend.figure_format = 'retina'retina格式会让图表在高分屏上显示得更清晰。另外,如果你要保存图表,用plt.savefig('filename.png', dpi=150, bbox_inches='tight'),bbox_inches='tight'可以去掉多余的空白边距。
还有一个技巧:在 Notebook 里,每个单元格执行完绘图代码后,最好加一个plt.show(),虽然 inline 模式下不加也会显示,但加上之后可以确保图表在正确的单元格下方显示,避免多个图表混在一起。
6. 常见问题与排查技巧实录
6.1 Token 失效与接口调用报错处理
最常见的问题就是 Token 失效或权限不足。报错信息通常是“抱歉,您没有访问该接口的权限”或者“Token 无效”。遇到这种情况,先检查 Token 是否复制完整,有没有多余的空格。然后确认你的积分是否足够调用该接口,有些接口需要较高的积分等级才能使用。
如果 Token 确认没问题但还是报错,可以尝试重新生成 Token。在 Tushare 个人主页的“接口TOKEN”页面,有一个“刷新”按钮,点击后会生成新的 Token,旧的立即失效。刷新之后记得更新你代码里的 Token。
还有一种情况是接口临时维护或网络波动。这种报错通常是“请求超时”或“服务不可用”。遇到这种问题,等几分钟再试,或者在代码里加一个重试机制:
import time def safe_call(func, max_retries=3, delay=5): for i in range(max_retries): try: return func() except Exception as e: print(f'第{i+1}次尝试失败: {e}') if i < max_retries - 1: time.sleep(delay) return None这个safe_call函数接受一个无参数的函数,失败时自动重试,最多重试max_retries次,每次间隔delay秒。你可以把任何 Tushare 调用包装进去。
6.2 数据为空或字段缺失的排查思路
有时候接口返回的是空 DataFrame,或者某些字段全是 NaN。排查思路可以按以下顺序进行:
第一,检查股票代码是否正确。Tushare 的代码格式是000001.SZ这种带后缀的,如果你只写了000001,接口会返回空。第二,检查日期范围是否合理。如果你查的是未来日期,或者查的是该股票上市之前的日期,返回空是正常的。第三,检查该股票是否在指定日期范围内有交易。如果股票停牌了很长时间,那段时间的数据就是空的。
字段缺失的情况通常是接口本身不返回该字段,或者该字段在特定条件下才有值。比如财务指标接口,有些指标只在年报中披露,季报中可能没有。这时候你需要根据分析目标决定是删除该字段还是用其他字段替代。
实操心得:我习惯在每次获取数据后立即打印
df.shape和df.isnull().sum(),这样能第一时间发现数据问题。如果shape是(0, 0),说明完全没拿到数据;如果某些列的非空计数明显偏低,说明该列有大量缺失。
6.3 Jupyter Notebook 卡顿与内存优化
当数据量大了之后,Jupyter Notebook 可能会变得卡顿,甚至崩溃。常见原因包括:DataFrame 太大占满内存、图表太多导致渲染缓慢、Notebook 里积累了太多输出内容。
优化的方法有几个。第一,及时删除不再使用的变量,用del df然后import gc; gc.collect()手动触发垃圾回收。第二,读取数据时只保留需要的列,用usecols参数或者获取后立即df = df[['col1', 'col2']]。第三,对于大数据集,考虑用dtype指定更节省内存的类型,比如把float64换成float32,把object换成category。
# 优化数据类型 df['pct_chg'] = df['pct_chg'].astype('float32') df['ts_code'] = df['ts_code'].astype('category')另外,定期重启 Kernel 并重新运行所有单元格,可以清理内存碎片。在菜单栏选择“Kernel” -> “Restart & Run All”即可。
6.4 常见报错速查表
| 报错信息 | 可能原因 | 解决方法 |
|---|---|---|
| 抱歉,您没有访问该接口的权限 | 积分不足或接口需要更高权限 | 查看接口文档的积分要求,提升积分或换用其他接口 |
| Token 无效 | Token 错误或已失效 | 重新复制或刷新 Token |
| 请求超时 | 网络问题或服务端繁忙 | 增加重试机制,延长超时时间 |
| 返回空 DataFrame | 代码格式错误、日期范围无数据、股票停牌 | 检查代码格式和日期范围 |
| KeyError: 'xxx' | 字段名拼写错误或该字段不存在 | 用df.columns查看可用字段 |
| MemoryError | 数据量超出内存 | 减少数据量、优化数据类型、分批处理 |
| SettingWithCopyWarning | 对切片赋值 | 用.copy()创建副本后再操作 |
提示:
SettingWithCopyWarning是 pandas 里最常见的警告之一。它出现的原因是你在一个切片上做赋值操作,pandas 不确定你是想修改原始数据还是副本。解决方法很简单,在切片后面加.copy(),明确告诉 pandas 你要一个独立的副本。
7. 从学习到实战:我的个人经验与建议
7.1 如何设计一个完整的数据分析小项目
学完基础操作之后,你需要一个完整的项目来串联所有知识点。我建议从这样一个问题开始:“某行业的多只股票在过去一年中,哪只表现最好?它们的收益和风险特征有什么差异?”这个问题涉及数据获取、清洗、指标计算、可视化和结论输出,是一个完整的数据分析闭环。
具体步骤可以这样设计:第一步,获取该行业所有股票的列表;第二步,批量获取这些股票过去一年的日线行情;第三步,计算每只股票的累计收益率、年化波动率、最大回撤;第四步,用表格和图表对比这些指标;第五步,写出你的分析结论,比如“A 股票收益最高但波动也最大,B 股票收益中等但回撤最小,适合风险偏好较低的投资者”。
这个项目不需要很复杂,但能让你走完整个流程。做完之后,你会对数据分析的每一个环节都有切身的体会,而不是停留在“知道”的层面。
7.2 我踩过的坑与总结出的实用技巧
第一个坑是“数据获取太多导致接口超限”。刚开始的时候,我想一次性获取所有股票十年的数据,结果调用了没几次就被限制访问了。后来我学会了分批获取,每次获取几十只,中间加 sleep,分几天完成。对于学习用途来说,不需要全量数据,选一个行业或者一个指数成分股就够了。
第二个坑是“忽略复权问题”。Tushare 的daily接口返回的是未复权价格,如果股票期间有分红送股,价格会出现跳空,影响收益率计算的准确性。解决方法是用pro_bar接口,设置adj='qfq'获取前复权数据:
df_adj = ts.pro_bar(ts_code='000001.SZ', adj='qfq', start_date='20230101', end_date='20231231')前复权是以当前价格为基准调整历史价格,适合做收益率分析。后复权是以历史价格为基准调整当前价格,适合做长期走势对比。我一般用前复权。
第三个坑是“在 Notebook 里写太长的代码块”。一个单元格里写几百行代码,调试起来非常痛苦。后来我养成了习惯:每个单元格只做一件事,获取数据一个单元格,清洗数据一个单元格,计算指标一个单元格,画图一个单元格。这样出问题的时候能快速定位是哪个环节出了错。
7.3 后续可以扩展的方向
当你熟练了基本流程之后,可以往几个方向扩展。一个是增加技术指标,比如 MACD、RSI、布林带,这些都可以用 pandas 计算出来。另一个是尝试不同的可视化方式,比如用 plotly 做交互式图表,或者用 pyecharts 做更美观的图表。还有一个方向是引入简单的统计分析,比如计算两只股票的相关性、做线性回归看趋势、用移动窗口计算滚动波动率等。
如果你对机器学习感兴趣,还可以尝试用历史数据做简单的预测模型,比如用线性回归预测下一日收盘价,或者用聚类算法对股票进行分类。不过这些属于进阶内容,建议先把基础的数据获取、清洗、计算、可视化练熟,再往深处走。
7.4 给初学者的学习节奏建议
最后说一下学习节奏。我见过很多人一开始热情很高,一天学五六个小时,结果三天之后就放弃了。数据分析是一个需要持续练习的技能,每天花 30 到 60 分钟,坚持两周,比突击三天效果好得多。
我的建议是:第一周,熟悉 Jupyter Notebook 的基本操作和 Tushare 的数据获取,能独立拿到一只股票的日线数据并画出走势图。第二周,学习数据清洗和指标计算,能计算移动平均线、收益率、波动率,并用表格和图表展示结果。第三周,尝试一个完整的小项目,从提出问题到得出结论,走完整个流程。三周之后,你就有了一个可以拿得出手的数据分析作品,也有了继续深入的基础。
我个人在实际操作中的体会是:数据分析最难的不是写代码,而是想清楚你要回答什么问题。代码只是工具,问题才是方向。每次开始分析之前,先花五分钟想清楚“我要回答什么问题”“需要哪些数据”“用什么指标衡量”,比直接打开 Notebook 就开始敲代码要高效得多。