在Pandas中如何实现复杂的筛选和查询

数据分析
Author

Tom

Published

April 11, 2023

在利用 Pandas 进行数据分析的过程当中,我们常常会碰到需要实现复杂查询或者筛选的场景。

比如说从全样本股票中,剔除 ST 和 *ST 股票,然后筛选属于中小板的股票。这篇文章总结我在科研实践过程当中,使用 Pandas 实现复杂筛选的方法。

首先导入 Pandas 和 akshare,以下的演示数据来自于从 akshare 调用的沪深京 A 股行情数据。

import numpy as np
import pandas as pd
import akshare as ak
data = ak.stock_zh_a_spot_em()
data.head()
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
0 1 603119 N浙荣 35.37 130.87 20.05 505082.0 1.561754e+09 72.85 37.54 ... NaN 73.63 79.87 6.46 9.903600e+09 2.426214e+09 -0.03 -0.34 130.87 130.87
1 2 301371 N敷尔佳 70.70 26.98 15.02 278191.0 2.015432e+09 19.83 80.04 ... NaN 73.92 44.38 5.64 2.828566e+10 2.660599e+09 0.11 0.41 26.98 26.98
2 3 301316 慧博云通 27.60 20.00 4.60 135775.0 3.483604e+08 21.04 27.60 ... 3.10 33.94 212.80 11.68 1.104028e+10 1.104274e+09 0.00 0.00 18.10 44.43
3 4 301289 国缆检测 43.62 20.00 7.27 75204.0 3.119352e+08 21.79 43.62 ... 13.27 29.67 56.11 3.86 3.402360e+09 1.105763e+09 0.00 0.00 28.26 35.55
4 5 300881 盛德鑫泰 40.98 20.00 6.83 118733.0 4.645422e+08 20.73 40.98 ... 2.57 40.73 56.02 5.48 4.507800e+09 1.194567e+09 0.00 0.00 23.81 42.05

5 rows × 23 columns

利用 loc 函数和布尔列表进行筛选

我最常用的一个做法是使用 loc 函数和布尔列表进行筛选。loc 函数可以接受布尔列表作为参数,而根据实际需求,我们有多种方式可以生成布尔列表,最普遍的是利用逻辑运算来生成布尔列表。

  • 逻辑运算产生布尔列表

比如,我们需要筛选出市净率大于 10 的股票,使用如下逻辑运算,可以产生一个布尔列表。

data.市净率 > 10
0       False
1       False
2        True
3       False
4       False
        ...  
5483    False
5484    False
5485    False
5486    False
5487    False
Name: 市净率, Length: 5488, dtype: bool

然后我们将这个布尔列表传入 loc 函数中,就可以筛选出市净率大于 10 的股票。

data.loc[data.市净率 > 10, :]
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
2 3 301316 慧博云通 27.60 20.00 4.60 135775.0 3.483604e+08 21.04 27.60 ... 3.10 33.94 212.80 11.68 1.104028e+10 1.104274e+09 0.00 0.00 18.10 44.43
31 32 000980 众泰汽车 4.84 10.00 0.44 5986844.0 2.750542e+09 11.59 4.84 ... 1.34 13.96 -32.52 10.85 2.440593e+10 2.075127e+10 0.00 0.00 60.80 13.62
32 33 000890 法尔胜 5.39 10.00 0.49 860438.0 4.285253e+08 15.51 5.39 ... 1.53 20.51 -45.72 165.44 2.261126e+09 2.261057e+09 0.00 0.00 23.06 40.00
46 47 300394 天孚通信 97.69 9.29 8.30 250569.0 2.361952e+09 11.84 98.88 ... 1.69 6.99 82.45 14.37 3.856400e+10 3.501563e+10 -0.02 -0.20 88.23 293.12
52 53 300025 华星创业 14.30 7.92 1.05 240567.0 3.384392e+08 8.68 14.50 ... 2.76 5.55 -327.75 10.83 7.275739e+09 6.201523e+09 0.00 0.00 32.78 201.05
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
5366 5367 002316 亚联发展 5.45 -4.55 -0.26 605077.0 3.320110e+08 4.73 5.69 ... 0.55 19.21 -108.92 42.25 2.142504e+09 1.716791e+09 0.00 0.18 19.26 55.27
5386 5387 688359 三孚新科 64.40 -4.87 -3.30 12630.0 8.158510e+07 9.00 68.10 ... 1.36 2.53 -122.05 12.81 5.984048e+09 3.217939e+09 0.00 0.42 -20.89 -4.58
5423 5424 600769 祥龙电业 9.18 -6.13 -0.60 144311.0 1.329581e+08 7.67 9.75 ... 2.30 3.85 249.21 42.27 3.442291e+09 3.442291e+09 -0.11 0.11 8.00 11.95
5424 5425 300114 中航电测 52.44 -6.16 -3.44 453818.0 2.432903e+09 5.71 55.49 ... 1.00 7.68 338.46 13.47 3.097948e+10 3.097948e+10 0.02 -0.06 3.86 412.61
5434 5435 002336 人人乐 17.47 -6.38 -1.19 470951.0 8.387931e+08 8.47 18.58 ... 1.01 12.62 -19.49 637.20 7.686800e+09 6.519367e+09 -0.11 0.17 47.05 -6.88

190 rows × 23 columns

也可以使用复杂的逻辑运算,比如我们筛选市净率大于 10 ,同时换手率大于 35% 的股票。

data.loc[(data.市净率 > 10) & (data.换手率 > 35), :]
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
5337 5338 000721 西安饮食 14.67 -4.12 -0.63 1547593.0 2.390809e+09 10.98 16.29 ... 1.9 35.37 -67.62 16.61 8.419323e+09 6.418153e+09 0.0 -0.07 16.71 -15.2

1 rows × 23 columns

使用 loc 函数也很容易实现对列的筛选,我们只需要将需要的列以列表形式传入即可。

data.loc[(data.市净率 > 10) & (data.换手率 > 35), ["代码", "名称", "最新价"]]
代码 名称 最新价
5337 000721 西安饮食 14.67
  • 函数判断产生布尔变量

第二种常用的手段是使用函数判断生成布尔变量。比如我们挑选上交所的股票,这里使用 startswith 函数来对代码这一列做判断。

data.loc[[code.startswith("6") for code in data.代码], :]
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
0 1 603119 N浙荣 35.37 130.87 20.05 505082.0 1.561754e+09 72.85 37.54 ... NaN 73.63 79.87 6.46 9.903600e+09 2.426214e+09 -0.03 -0.34 130.87 130.87
10 11 688622 禾信仪器 34.85 10.95 3.44 27626.0 9.574625e+07 9.87 36.00 ... 9.74 7.18 -37.27 4.74 2.439417e+09 1.341637e+09 -0.03 -0.03 -5.76 7.23
11 12 688776 国光电气 87.80 10.66 8.46 22037.0 1.848112e+08 11.33 87.99 ... 4.84 6.52 116.36 5.39 9.516064e+09 2.968588e+09 -0.22 1.09 -5.88 -29.79
14 15 600239 云南城投 2.92 10.19 0.27 1178068.0 3.334986e+08 9.06 2.92 ... 1.40 7.34 7.63 2.89 4.688606e+09 4.688606e+09 0.00 0.00 28.07 51.30
16 17 600577 精达股份 4.79 10.11 0.44 1857359.0 8.645382e+08 10.80 4.79 ... 9.05 9.31 24.64 2.07 9.959123e+09 9.559956e+09 0.00 0.00 14.05 16.26
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
5463 5464 688566 吉贝尔 29.59 -8.53 -2.76 48715.0 1.487687e+08 9.83 32.38 ... 2.78 2.61 36.38 3.28 5.531602e+09 5.531602e+09 -0.03 0.03 -7.15 39.71
5466 5467 688646 C逸飞 48.67 -8.98 -4.80 67526.0 3.338214e+08 5.29 51.41 ... 0.56 37.75 133.46 2.95 4.631564e+09 8.706737e+08 -0.06 -0.21 4.00 4.00
5474 5475 603383 顶点软件 57.96 -10.00 -6.44 78618.0 4.696362e+08 8.25 63.27 ... 1.75 4.66 112.07 7.93 9.923753e+09 9.787619e+09 0.00 0.00 10.09 35.29
5475 5476 603778 乾景园林 4.85 -10.02 -0.54 518583.0 2.522182e+08 1.86 4.95 ... 1.93 8.07 67.14 3.02 3.117857e+09 3.117857e+09 0.00 0.00 -19.44 -29.91
5476 5477 603682 锦和商管 7.52 -10.05 -0.84 625823.0 4.835391e+08 5.86 8.01 ... 1.07 13.24 38.32 3.10 3.553200e+09 3.553200e+09 0.00 0.00 40.04 39.00

2339 rows × 23 columns

再比如,剔除 ST 股。

data.loc[~np.array([name.startswith("ST") for name in data.名称]), :]
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
0 1 603119 N浙荣 35.37 130.87 20.05 505082.0 1.561754e+09 72.85 37.54 ... NaN 73.63 79.87 6.46 9.903600e+09 2.426214e+09 -0.03 -0.34 130.87 130.87
1 2 301371 N敷尔佳 70.70 26.98 15.02 278191.0 2.015432e+09 19.83 80.04 ... NaN 73.92 44.38 5.64 2.828566e+10 2.660599e+09 0.11 0.41 26.98 26.98
2 3 301316 慧博云通 27.60 20.00 4.60 135775.0 3.483604e+08 21.04 27.60 ... 3.10 33.94 212.80 11.68 1.104028e+10 1.104274e+09 0.00 0.00 18.10 44.43
3 4 301289 国缆检测 43.62 20.00 7.27 75204.0 3.119352e+08 21.79 43.62 ... 13.27 29.67 56.11 3.86 3.402360e+09 1.105763e+09 0.00 0.00 28.26 35.55
4 5 300881 盛德鑫泰 40.98 20.00 6.83 118733.0 4.645422e+08 20.73 40.98 ... 2.57 40.73 56.02 5.48 4.507800e+09 1.194567e+09 0.00 0.00 23.81 42.05
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
5483 5484 301024 霍普股份 30.61 -11.04 -3.80 70147.0 2.218308e+08 10.40 33.83 ... 0.79 39.55 -73.00 3.12 1.946337e+09 5.429449e+08 0.03 -0.16 23.68 40.41
5484 5485 300554 三超新材 18.55 -11.58 -2.43 113466.0 2.149169e+08 7.53 20.08 ... 7.79 14.82 233.03 2.67 2.118625e+09 1.420001e+09 -0.16 -0.27 0.54 -24.90
5485 5486 301381 赛维时代 40.62 -11.98 -5.53 142905.0 6.005835e+08 11.09 45.72 ... 0.86 45.50 78.18 7.29 1.625206e+10 1.275912e+09 0.02 -0.51 98.63 98.63
5486 5487 301015 百洋医药 22.16 -11.99 -3.02 108565.0 2.501874e+08 14.26 25.30 ... 6.25 9.17 23.76 5.75 1.163622e+10 2.623190e+09 0.00 0.41 -18.38 -3.99
5487 5488 301172 C君逸 43.91 -12.16 -6.08 179476.0 8.451293e+08 14.96 51.30 ... 0.86 64.87 -314.59 3.87 5.409712e+09 1.214778e+09 -0.02 -0.52 40.15 40.15

5406 rows × 23 columns

我们也可以自定义函数,来实现更复杂的判断需求。比如,对于上面的剔除 ST 股,我们也可以通过自定义函数实现:

def filter_ST(name):
    return not name.startswith("ST")
data.loc[data.loc[:,'名称'].apply(lambda name: filter_ST(name)), :]
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
0 1 603119 N浙荣 35.37 130.87 20.05 505082.0 1.561754e+09 72.85 37.54 ... NaN 73.63 79.87 6.46 9.903600e+09 2.426214e+09 -0.03 -0.34 130.87 130.87
1 2 301371 N敷尔佳 70.70 26.98 15.02 278191.0 2.015432e+09 19.83 80.04 ... NaN 73.92 44.38 5.64 2.828566e+10 2.660599e+09 0.11 0.41 26.98 26.98
2 3 301316 慧博云通 27.60 20.00 4.60 135775.0 3.483604e+08 21.04 27.60 ... 3.10 33.94 212.80 11.68 1.104028e+10 1.104274e+09 0.00 0.00 18.10 44.43
3 4 301289 国缆检测 43.62 20.00 7.27 75204.0 3.119352e+08 21.79 43.62 ... 13.27 29.67 56.11 3.86 3.402360e+09 1.105763e+09 0.00 0.00 28.26 35.55
4 5 300881 盛德鑫泰 40.98 20.00 6.83 118733.0 4.645422e+08 20.73 40.98 ... 2.57 40.73 56.02 5.48 4.507800e+09 1.194567e+09 0.00 0.00 23.81 42.05
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
5483 5484 301024 霍普股份 30.61 -11.04 -3.80 70147.0 2.218308e+08 10.40 33.83 ... 0.79 39.55 -73.00 3.12 1.946337e+09 5.429449e+08 0.03 -0.16 23.68 40.41
5484 5485 300554 三超新材 18.55 -11.58 -2.43 113466.0 2.149169e+08 7.53 20.08 ... 7.79 14.82 233.03 2.67 2.118625e+09 1.420001e+09 -0.16 -0.27 0.54 -24.90
5485 5486 301381 赛维时代 40.62 -11.98 -5.53 142905.0 6.005835e+08 11.09 45.72 ... 0.86 45.50 78.18 7.29 1.625206e+10 1.275912e+09 0.02 -0.51 98.63 98.63
5486 5487 301015 百洋医药 22.16 -11.99 -3.02 108565.0 2.501874e+08 14.26 25.30 ... 6.25 9.17 23.76 5.75 1.163622e+10 2.623190e+09 0.00 0.41 -18.38 -3.99
5487 5488 301172 C君逸 43.91 -12.16 -6.08 179476.0 8.451293e+08 14.96 51.30 ... 0.86 64.87 -314.59 3.87 5.409712e+09 1.214778e+09 -0.02 -0.52 40.15 40.15

5406 rows × 23 columns

  • eval 函数产生布尔变量

我们也可以使用 eval 函数来产生布尔变量:

data.loc[data.eval("市净率 > 10 and 换手率 > 35"), :]
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
5337 5338 000721 西安饮食 14.67 -4.12 -0.63 1547593.0 2.390809e+09 10.98 16.29 ... 1.9 35.37 -67.62 16.61 8.419323e+09 6.418153e+09 0.0 -0.07 16.71 -15.2

1 rows × 23 columns

利用 merge 函数筛选

有时候,我们需要根据样本来筛选数据,这时候可以使用 merge 函数来进行合并,合并过程也就是数据的筛选过程。比如,我们需要对样本股票的历史数据进行回归,需要从全样本历史数据中筛选出样本股票的历史收益率数据。下面的例子,我们首选读取全样本股票的历史收益率数据,然后筛选出市净率大于 10 ,同时换手率大于 35% 的股票历史收益率数据。

r = pd.read_csv("TRD_Dalyr.csv", dtype={'Stkcd':str})
r.head()
Stkcd Trddt Dretwd
0 000001 2018-01-02 0.030075
1 000001 2018-01-03 -0.027007
2 000001 2018-01-04 -0.006002
3 000001 2018-01-05 0.003774
4 000001 2018-01-08 -0.025564
banks = data.loc[(data.市净率 > 10) & (data.换手率 > 35), ["代码", "名称"]]
banks
代码 名称
5337 000721 西安饮食
r.merge(banks, left_on="Stkcd", right_on="代码", how="inner")
Stkcd Trddt Dretwd 代码 名称
0 000721 2018-01-02 0.003540 000721 西安饮食
1 000721 2018-01-03 0.010582 000721 西安饮食
2 000721 2018-01-04 0.000000 000721 西安饮食
3 000721 2018-01-05 0.001745 000721 西安饮食
4 000721 2018-01-08 -0.010453 000721 西安饮食
... ... ... ... ... ...
1015 000721 2022-03-10 0.042986 000721 西安饮食
1016 000721 2022-03-11 -0.004338 000721 西安饮食
1017 000721 2022-03-14 0.008715 000721 西安饮食
1018 000721 2022-03-15 -0.036717 000721 西安饮食
1019 000721 2022-03-16 0.067265 000721 西安饮食

1020 rows × 5 columns

利用 query 函数进行筛选

query 是 Pandas 提供的一个方法,使用布尔表达式对 DataFrame 进行查询,这个布尔表达式支持一个文本字符串。比如,查询市净率大于 10,同时换手率大于 35% 的股票:

data.query("市净率 > 10 and 换手率 > 35")
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
5337 5338 000721 西安饮食 14.67 -4.12 -0.63 1547593.0 2.390809e+09 10.98 16.29 ... 1.9 35.37 -67.62 16.61 8.419323e+09 6.418153e+09 0.0 -0.07 16.71 -15.2

1 rows × 23 columns

query 也支持传入变量,使用 @ 传入。

name = "西安饮食"
data.query("名称 == @name")
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 ... 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至今涨跌幅
5337 5338 000721 西安饮食 14.67 -4.12 -0.63 1547593.0 2.390809e+09 10.98 16.29 ... 1.9 35.37 -67.62 16.61 8.419323e+09 6.418153e+09 0.0 -0.07 16.71 -15.2

1 rows × 23 columns