3. 数据读取与初步探索:read_csv/read_excel、head/tail/info/describe、数据类型概览

好,咱们正式开始动手了。

前面两章聊了架构和工具链,说白了都是热身。这一章才是真正跟数据打交道的第一步——把文件读进来,然后快速摸清它的底细。

我个人习惯,拿到一个新数据集,第一件事不是跑模型,而是先看看它长什么样。就像你拿到一本新书,总得先翻翻目录、看看前言吧?数据探索就是这个道理。

3.1 读取文件:read_csv 与 read_excel

投研平台里最常见的数据源就是 CSV 和 Excel。Pandas 提供了两个非常顺手的函数:read_csvread_excel

先看 CSV 的读取:

import pandas as pd

# 最简单的读取
df = pd.read_csv('stock_data.csv')

# 带参数读取,我建议你养成这个习惯
df = pd.read_csv(
    'stock_data.csv',
    encoding='utf-8',        # 避免乱码
    parse_dates=['date'],    # 日期列自动解析
    dtype={'code': str},     # 股票代码强制为字符串
    low_memory=False         # 大文件时避免类型推断警告
)

这里有个坑,我曾经遇到过:某次从 Wind 导出的 CSV,股票代码列被 Excel 自动截断了前导零。比如 "000001" 变成了 "1"。所以 dtype={'code': str} 这行,我几乎每次都会写。

再看 Excel 读取:

# 读取第一个sheet
df = pd.read_excel('fund_data.xlsx', sheet_name=0)

# 读取指定sheet
df = pd.read_excel('fund_data.xlsx', sheet_name='持仓明细')

# 大Excel文件,建议用openpyxl引擎
df = pd.read_excel('large_file.xlsx', engine='openpyxl')
小技巧: 如果你不确定 Excel 文件里有哪些 sheet,可以用 pd.ExcelFile('file.xlsx').sheet_names 先列出来。我每次接新数据源都会先跑这行,心里有个底。

3.2 初步探索:head / tail / info / describe

数据读进来了,接下来就是“看一眼”。Pandas 提供了四个最常用的方法,我管它们叫“四件套”。

3.2.1 head() 和 tail()

看头看尾,快速确认数据格式对不对。

# 看前5行(默认)
df.head()

# 看前10行
df.head(10)

# 看后5行
df.tail()

# 看后3行
df.tail(3)

为什么要看 tail?因为很多数据源会在文件末尾加汇总行或者空行。我遇到过好几次,最后一行是 "合计:1000条",如果不看 tail,直接做聚合计算,结果就偏了。

3.2.2 info() —— 数据集的“体检报告”

info() 是我用得最多的方法,没有之一。它告诉你:有多少行、多少列、每列的类型、有没有空值。

df.info()

输出大概长这样:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000 entries, 0 to 9999
Data columns (total 8 columns):
 #   Column   Non-Null Count  Dtype         
---  ------   --------------  -----         
 0   date     10000 non-null  datetime64[ns]
 1   code     10000 non-null  object        
 2   name     10000 non-null  object        
 3   close    9995 non-null   float64       
 4   volume   10000 non-null  int64         
 5   amount   10000 non-null  float64       
 6   pe_ttm   8000 non-null   float64       
 7   pb       7500 non-null   float64       
dtypes: datetime64[ns](1), float64(4), int64(1), object(2)
memory usage: 625.0+ KB

看到没?close 列有 5 个空值,pe_ttmpb 缺失更多。这就是下一步清洗的信号。

重点: info() 里的 Dtype 列,直接告诉你每列的数据类型。如果发现本该是数字的列显示为 object,那就要警惕了——很可能混入了文本,比如 "N/A" 或者 "--"。

3.2.3 describe() —— 数值列的统计摘要

describe() 自动对数值列做统计:计数、均值、标准差、四分位数等。

df.describe()

输出示例:

close volume amount pe_ttm pb
count 9995.0 10000.0 10000.0 8000.0 7500.0
mean 25.36 1.2e7 3.1e8 18.5 2.1
std 12.04 8.5e6 2.2e8 15.2 1.8
min 1.02 0 0 -50.3 0.1
25% 15.80 5.2e6 1.1e8 8.2 0.9
50% 22.10 1.0e7 2.5e8 15.0 1.6
75% 32.50 1.8e7 4.5e8 25.3 2.8
max 98.76 9.9e7 2.1e9 200.0 15.0

这里要注意几个点:

  • count 不一致:说明有缺失值,跟 info() 对得上
  • min 出现负数:比如 pe_ttm 最小值是 -50.3,这可能是亏损公司的数据,也可能是异常值
  • max 异常大:pe_ttm 最大值 200,明显偏离正常范围,需要进一步排查
警告: describe() 默认只统计数值列。如果你想知道分类列的分布,可以用 df['column'].value_counts()。我曾经因为没检查分类列,结果发现 "行业" 列里混入了 "行业" 这个表头本身,整整多了一行脏数据。

3.3 数据类型概览:dtypes 与 astype

数据类型是数据清洗的基石。Pandas 里常见的数据类型有:

  • int64:整数,比如成交量
  • float64:浮点数,比如股价、市盈率
  • object:字符串/混合类型,比如股票代码、股票名称
  • datetime64:日期时间,比如交易日期
  • bool:布尔值,比如是否ST

查看所有列的类型:

df.dtypes

如果需要转换类型,用 astype

# 把股票代码转为字符串
df['code'] = df['code'].astype(str)

# 把日期列转为datetime
df['date'] = pd.to_datetime(df['date'])

# 把市盈率转为浮点数(如果混入了文本,会报错)
df['pe_ttm'] = pd.to_numeric(df['pe_ttm'], errors='coerce')

这里 errors='coerce' 是个好参数。它会把无法转换的值变成 NaN,而不是直接报错。我处理投研数据时,经常遇到市盈率列里混入了 "--" 或者 "N/A",用这个参数就能优雅地处理。

个人经验: 我建议你在读取数据后,立刻用 df.dtypes 扫一遍。如果发现本该是数字的列显示为 object,那八成是数据里有脏值。这时候别急着转类型,先用 df[df['列名'].str.contains('[a-zA-Z]', na=False)] 把异常行揪出来看看。

3.4 知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作数据探索的“操作手册”。

数据读取与初步探索 · 知识体系 读取数据 read_csv / read_excel 初步概览 head / tail 数据体检 info / describe 数据类型检查 dtypes / astype / to_numeric 发现潜在问题 缺失值 · 异常值 · 类型错误 脏数据 · 格式不一致 进入数据清洗流程 每一步都对应一个或几个 Pandas 方法,建议按这个顺序操作

这张图其实就概括了本章的全部内容。你每次拿到新数据,都可以按这个流程走一遍。我自己的习惯是:先读进来,看头看尾,跑 info 和 describe,然后检查 dtypes。这一套下来,数据的基本情况就摸得七七八八了。

嗯,数据探索这一步,说白了就是“知己知彼”。你只有知道数据长什么样、有什么坑,后面的清洗才能有的放矢。别急着往下跳,多花几分钟做探索,后面能省几小时。


专注资料整理