3. 数据读取与初步探索:读取CSV/Excel文件、查看数据基本信息
好了,咱们正式开始动手了。
上一章聊了聊景气度数据长什么样,这一章咱们就直接把数据请进 Python 里来。我个人习惯,拿到任何数据文件,第一件事不是跑模型,而是先把它读进来,然后好好“打量”一番——看看它长什么样、有没有缺胳膊少腿、数据类型对不对。
说白了,这一步就像你拿到一份新简历,先扫一眼基本信息,心里有个底。
3.1 读取CSV文件:最常用的姿势
CSV 文件在量化投资领域太常见了。交易所的日频数据、行业分类数据、宏观指标,十有八九都是 CSV 格式。我一般用 pandas 的 read_csv 函数,简单直接。
import pandas as pd
# 读取CSV文件
df = pd.read_csv('景气度数据.csv')
print('数据读取成功!')
嗯,就这么一行。但这里有个坑——编码问题。我在项目中遇到过好几次,从万得导出的 CSV 文件,默认编码是 gbk 或 gb2312,直接用 read_csv 读会报错。解决办法很简单:
df = pd.read_csv('景气度数据.csv', encoding='gbk')
# 如果还不行,试试 encoding='gb2312' 或 encoding='utf-8'
r 变成原始字符串,或者用双反斜杠。比如 r'C:\data\景气度.csv'。
3.2 读取Excel文件:多Sheet也不怕
Excel 文件在业务部门用得更多。很多券商的研究报告、行业数据库,都喜欢用 Excel 格式。读取 Excel 用 pd.read_excel():
# 读取Excel文件
df = pd.read_excel('景气度数据.xlsx', sheet_name='Sheet1')
如果你要读取多个 Sheet,可以传一个列表:
# 读取多个Sheet
dfs = pd.read_excel('景气度数据.xlsx', sheet_name=['Sheet1', 'Sheet2'])
# 返回的是一个字典,key是Sheet名,value是DataFrame
我曾经有一次处理一个季度数据,Excel 文件里有 12 个 Sheet,每个 Sheet 代表一个月的行业景气度。用 sheet_name=None 一次性全读进来,然后循环处理,效率很高。
nrows=1000 参数先读前 1000 行看看结构,避免一次性加载太慢。
3.3 查看数据基本信息:head、info、describe
数据读进来了,接下来就是“打量”它。我一般按这个顺序来:
3.3.1 用 head() 看前几行
# 查看前5行
df.head()
这能让你快速了解数据长什么样——有哪些列、数据大概是什么格式。我习惯先看 head(10),多几行更容易发现异常。
3.3.2 用 info() 看数据类型和缺失值
# 查看数据基本信息
df.info()
这个函数会告诉你:
- 总共有多少行、多少列
- 每列的数据类型(int64、float64、object 等)
- 每列有多少个非空值
- 内存占用情况
举个例子,输出可能是这样的:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1200 entries, 0 to 1199
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 日期 1200 non-null object
1 行业 1200 non-null object
2 景气度指数 1150 non-null float64
3 营收增速 1100 non-null float64
4 利润增速 1080 non-null float64
5 库存周转 1200 non-null float64
6 产能利用率 1050 non-null float64
7 毛利率 1120 non-null float64
dtypes: float64(6), object(2)
memory usage: 75.1 KB
看到没?日期列是 object 类型,这其实不对。日期应该转成 datetime 类型,否则后面做时间序列分析会出问题。这个我们下一章会处理。
另外,景气度指数只有 1150 个非空值,总共有 1200 行,说明有 50 个缺失值。嗯,这里要注意,缺失值怎么处理,后面会专门讲。
3.3.3 用 describe() 看统计摘要
# 查看数值型列的统计摘要
df.describe()
输出类似这样:
| 景气度指数 | 营收增速 | 利润增速 | 库存周转 | 产能利用率 | 毛利率 | |
|---|---|---|---|---|---|---|
| count | 1150.0 | 1100.0 | 1080.0 | 1200.0 | 1050.0 | 1120.0 |
| mean | 52.3 | 8.5 | 6.2 | 45.1 | 78.3 | 22.5 |
| std | 15.2 | 12.3 | 18.7 | 8.9 | 10.5 | 5.6 |
| min | 10.0 | -30.5 | -45.2 | 20.0 | 45.0 | 8.0 |
| 25% | 40.0 | 2.0 | -5.0 | 38.0 | 70.0 | 18.0 |
| 50% | 52.0 | 8.0 | 5.0 | 45.0 | 80.0 | 22.0 |
| 75% | 65.0 | 15.0 | 18.0 | 52.0 | 88.0 | 27.0 |
| max | 90.0 | 55.0 | 60.0 | 70.0 | 98.0 | 40.0 |
这个表能告诉你很多信息:
- 均值和中位数:如果两者差距很大,说明数据可能有偏态分布
- 最小值/最大值:看看有没有离谱的异常值。比如营收增速最小是 -30.5%,最大是 55%,这还算正常。但如果出现 -9999 这种,那肯定是脏数据
- count 列:再次确认缺失值情况
3.4 数据类型概览:别让类型坑了你
数据类型是数据清洗的第一步。我见过太多人栽在这个坑里——明明应该是数值的列,读进来却是字符串;明明应该是日期的列,读进来却是 object。
用 dtypes 属性可以快速查看每列的类型:
# 查看所有列的数据类型
print(df.dtypes)
输出:
日期 object
行业 object
景气度指数 float64
营收增速 float64
利润增速 float64
库存周转 float64
产能利用率 float64
毛利率 float64
dtype: object
常见的类型问题:
- 数值列是 object 类型:可能是因为数据里混入了逗号(比如 "1,234")或百分号(比如 "22.5%")
- 日期列是 object 类型:需要转成 datetime,否则没法做时间序列分析
- 整数列是 float64 类型:可能是因为有缺失值,pandas 会自动转成 float
怎么处理?举个例子:
# 把日期列转成 datetime 类型
df['日期'] = pd.to_datetime(df['日期'])
# 如果数值列里有百分号,先去掉再转
df['毛利率'] = df['毛利率'].str.replace('%', '').astype(float) / 100
errors='coerce' 参数,让无法转换的值变成 NaN。
3.5 本章知识体系
下面这张图总结了本章的核心逻辑,你可以把它当作一个检查清单:
这张图把本章的三个核心步骤串起来了:读取数据 → 查看基本信息 → 检查数据类型。每一步都可能遇到坑,但只要你按这个流程走一遍,基本不会漏掉关键信息。
好了,数据已经读进来了,基本信息也看过了。下一章咱们聊聊怎么处理那些烦人的缺失值和异常值——这可是数据清洗的重头戏。