【script】数据处理的瑞士军刀 pandas

一、Pandas介绍

在Python中, pandas 包含了高级的数据结构 Series 和 DataFrame ,使得在Python中处理数据变得非常方便、快速和简单。

pandas 不同的版本之间存在一些不兼容性,为此,我们需要清楚使用的是哪一个版本的 pandas 。

import pandas as pd
print(pd.__version__)

1.3.1

pandas 主要的两个数据结构是 Series 和 DataFrame ,我们先导入它们以及相关模块:

import numpy as np
import pandas as pd
from pandas import Series, DataFrame

二、Pandas数据结构:Series

Series 也可以叫做 序列。从一般意义上来讲, Series 可以简单地被认为是一维的数组。 Series 和一维数组最主要的区别在于 Series 类型具有索引( index ),可以和另一个编程中常见的数据结构哈希(Hash)联系起来。其次,Series 的元素通常类型统一,而一维数组可以是不同类型的元素组成。

2.1 创建 Series

创建一个 Series 的基本格式是 s = Series(data, index=index, name=name) ,以下给出几个创建 Series 的例子 :

a = np.random.randn(5)
print("a is an array:")
print(a)
s = Series(a)
print("s is a Series:")
print(s)

a is an array:
[-0.3007983   0.2893125  -0.10106809 -1.06076531  0.29202818]
s is a Series:
0   -0.300798
1    0.289313
2   -0.101068
3   -1.060765
4    0.292028
dtype: float64

可以在创建 Series 时添加 index ,并可使用 Series.index 查看具体 的 index 。需要注意的一点是,当从数组创建 Series 时,若指定 index ,那 么 index 长度要和 data 的长度一致:

s = Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print(s)
print(s.index)

a    1.192283
b    1.477963
c   -0.386441
d    1.622310
e    0.845787
dtype: float64
Index(['a', 'b', 'c', 'd', 'e'], dtype='object')

创建 Series 的另一个可选项是 name ,可指定 Series 的名称,可 用 Series.name 访问。在随后的 DataFrame 中,每一列的列名在该列被单独取 出来时就成了 Series 的名称:

s = Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'],
name='my_series')
print(s)
print(s.name)

a   -2.240155
b    0.258177
c    0.343206
d    1.220887
e   -0.153971
Name: my_series, dtype: float64
my_series

Series 还可以从字典( dict )创建:

d = {'a': 0., 'b': 1, 'c': 2}
print("d is a dict:")
print(d)
s = Series(d)
print("s is a Series:")
print(s)

d is a dict:
{'a': 0.0, 'b': 1, 'c': 2}
s is a Series:
a    0.0
b    1.0
c    2.0
dtype: float64

让我们来看看使用字典创建 Series 时指定 index 的情形( index 长度不必和 字典相同):

s = Series(d, index=['b', 'c', 'd', 'a'])
print(s)

b    1.0
c    2.0
d    NaN
a    0.0
dtype: float64

我们可以观察到两点:一是字典创建的 Series ,数据将按 index 的顺序重新排 列;二是 index 长度可以和字典长度不一致,如果多了的话, pandas 将自动为 多余的 index 分配 NaN (not a number, pandas 中数据缺失的标准记号),当 然 index 少的话就截取部分的字典内容。

如果数据就是一个单一的变量,如数字4,那么 Series 将重复这个变量:

s = Series(4., index=['a', 'b', 'c', 'd', 'e'])
print(s)

a    4.0
b    4.0
c    4.0
d    4.0
e    4.0
dtype: float64

2.2 Series 数据的访问

访问 Series 数据可以和数组一样使用下标,也可以像字典一样使用索引,还可以使用一些条件过滤:

s = Series(np.random.randn(10),index=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'])print(s[0])0.20339387093082803
print(s[[0]])a    0.906648dtype: float64

注:s[0] 和 s[[0]] 的区别在于,s[0] 仅取值,而 s[[0]] 是取行,会保留原来的序列类型

print(s[:2])a   -2.028119b    0.061965dtype: float64
print(s[[2,0,4]])c   -0.526092a    0.484422e    1.571355dtype: float64
print(s[['e', 'i']])e    0.435927i    1.045612dtype: float64
print(s[s > 0.5])c    0.995218e    0.858984h    0.942102i    0.675896dtype: float64
print('e' in s)True

三、Pandas数据结构: DataFrame

DataFrame也叫数据结构。在使用 DataFrame 之前,我们说明一下 DataFrame 的特性。 DataFrame 是将数个 Series 按列合并而成的二维数据结构,每一列单独取出来是一个 Series ,这和SQL数据库中取出的数据是很类似的。所以,按列对一个 DataFrame 进行处理更为方便,用户在编程时注意培养按列构建数据的思维。 DataFrame 的优势在于可以方便地处理不同类型的列,因此,就不要考虑如何对一个全是浮点数的 DataFrame 求逆之类的问题了,处理这种问题还是把数据 存成 NumPy 的 matrix 类型比较便利一些。

3.1 创建 DataFrame

首先来看如何从字典创建 DataFrame 。 DataFrame 是一个二维的数据结构,是 多个 Series 的集合体。我们先创建一个值是 Series 的字典,并转换为 DataFrame :

d = {'one': Series([1., 2., 3.], index=['a', 'b', 'c']), 'two':Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}df = DataFrame(d)print(df)   one  twoa  1.0  1.0b  2.0  2.0c  3.0  3.0d  NaN  4.0

可以指定所需的行和列,若字典中不含有对应的元素,则置为 NaN :

df = DataFrame(d, index=['r', 'd', 'a'], columns=['two', 'three'])print(df)   two threer  NaN   NaNd  4.0   NaNa  1.0   NaN

可以使用 dataframe.index 和 dataframe.columns 来查看 DataFrame 的行和列, dataframe.values 则以数组的形式返回 DataFrame 的元素:

print("DataFrame index:")print(df.index)print("DataFrame columns:")print(df.columns)print("DataFrame values:")print(df.values)DataFrame index:Index(['a', 'b', 'c', 'd'], dtype='object')DataFrame columns:Index(['one', 'two'], dtype='object')DataFrame values:[[ 1.  1.] [ 2.  2.] [ 3.  3.] [nan  4.]]

DataFrame 也可以从值是数组的字典创建,但是各个数组的长度需要相同:

d = {'one': [1., 2., 3., 4.], 'two': [4., 3., 2., 1.]}df = DataFrame(d, index=['a', 'b', 'c', 'd'])print(df)   one  twoa  1.0  4.0b  2.0  3.0c  3.0  2.0d  4.0  1.0

值非数组时,没有这一限制,并且缺失值补成 NaN :

d= [{'a': 1.6, 'b': 2}, {'a': 3, 'b': 6, 'c': 9}]df = DataFrame(d)print(df)     a  b    c0  1.6  2  NaN1  3.0  6  9.0

在实际处理数据时,有时需要创建一个空的 DataFrame ,可以这么做:

df = DataFrame()print(df)Empty DataFrameColumns: []Index: []

另一种创建 DataFrame 的方法十分有用,那就是使用 concat 函数基于 Series 或者 DataFrame 创建一个 DataFrame

a = Series(range(5))b = Series(np.linspace(4, 20, 5))df = pd.concat([a, b], axis=1)print(df)   0     10  0   4.01  1   8.02  2  12.03  3  16.04  4  20.0

其中的 axis=1 表示按行进行合并, axis=0 表示按列合并,并且, Series 都处理成一列,所以这里如果选 axis=0 的话,将得到一个 10×1 的 DataFrame 。 下面这个例子展示了如何按行合并 DataFrame 成一个大的 DataFrame :

df = DataFrame()index = ['alpha', 'beta', 'gamma', 'delta', 'eta']for i in range(5):    a = DataFrame([np.linspace(i, 5*i, 5)], index=[index[i]])    df = pd.concat([df, a], axis=0)print(df)         0    1     2     3     4alpha  0.0  0.0   0.0   0.0   0.0beta   1.0  2.0   3.0   4.0   5.0gamma  2.0  4.0   6.0   8.0  10.0delta  3.0  6.0   9.0  12.0  15.0eta    4.0  8.0  12.0  16.0  20.0

3.2 DataFrame 数据的访问

首先,再次强调一下 DataFrame 是以列作为操作的基础的,全部操作都想象成先 从 DataFrame 里取一列,再从这个 Series 取元素即可。可以 用 datafrae.column_name 选取列,也可以使用 dataframe[] 操作选取列,我 们可以马上发现前一种方法只能选取一列,而后一种方法可以选择多列。 若 DataFrame 没有列名, [] 可以使用非负整数,也就是“下标”选取列;若有列 名,则必须使用列名选取,另外 datafrae.column_name 在没有列名的时候是无效的:

print(df[1])print(type(df[1]))alpha    0.0beta     2.0gamma    4.0delta    6.0eta      8.0Name: 1, dtype: float64<class 'pandas.core.series.Series'>
print(df[[1]])print(type(df[[1]]))         1alpha  0.0beta   2.0gamma  4.0delta  6.0eta    8.0<class 'pandas.core.frame.DataFrame'>

注:与2.2节中的 Series 类似,df[1] 获取某一列,类型变为 Series;df[[1]] 获取某一列,保持 DataFrame 结构

df.columns = ['a', 'b', 'c', 'd', 'e']print(df['b'])print(type(df['b']))alpha    0.0beta     2.0gamma    4.0delta    6.0eta      8.0Name: b, dtype: float64<class 'pandas.core.series.Series'>
print(df[['a', 'd']])print(type(df[['a', 'd']]))         a     dalpha  0.0   0.0beta   1.0   4.0gamma  2.0   8.0delta  3.0  12.0eta    4.0  16.0<class 'pandas.core.frame.DataFrame'>

以上代码使用了 dataframe.columns 为 DataFrame 赋列名,并且我们看到单独 取一列出来,其数据结构显示的是 Series ,取两列及两列以上的结果仍然是 DataFrame 。访问特定的元素可以如 Series 一样使用下标或者是索引:

print(df['b'][2])print(df['b']['gamma'])4.04.0

若需要选取行,可以使用 dataframe.iloc 按下标选取,或者使 用 dataframe.loc 按标签选取:

print(df.iloc[1])print(df.loc['beta'])a    1.0b    2.0c    3.0d    4.0e    5.0Name: beta, dtype: float64a    1.0b    2.0c    3.0d    4.0e    5.0Name: beta, dtype: float64

注:loc 基于标签,iloc 基于下标。如果 iloc 使用标签会报错,反之 loc 使用下标会报错。

例:正确用法为,df.iloc[0][0] 或 df.loc[‘a’][‘beta’]

选取行还可以使用切片的方式或者是布尔类型的向量:

print("Selecting by slices:")print(df[1:3])bool_vec = [True, False, True, True, False]print("Selecting by boolean vector:")print(df[bool_vec])Selecting by slices:         a    b    c    d     ebeta   1.0  2.0  3.0  4.0   5.0gamma  2.0  4.0  6.0  8.0  10.0Selecting by boolean vector:         a    b    c     d     ealpha  0.0  0.0  0.0   0.0   0.0gamma  2.0  4.0  6.0   8.0  10.0delta  3.0  6.0  9.0  12.0  15.0

行列组合起来选取数据:

# 先列后行print(df[['b', 'd']].iloc[[1, 3]])# 先行后列print(df.iloc[[1, 3]][['b', 'd']])# 同上df.iloc[1, 3]['b', 'd']# 先列后行print(df[['b', 'd']].loc[['beta', 'delta']])# 先行后列print(df.loc[['beta', 'delta']][['b', 'd']])# 同上df.loc['beta', 'delta']['b', 'd']         b     dbeta   2.0   4.0delta  6.0  12.0         b     dbeta   2.0   4.0delta  6.0  12.0         b     dbeta   2.0   4.0delta  6.0  12.0         b     dbeta   2.0   4.0delta  6.0  12.0

如果不是需要访问特定行列,而只是某个特殊位置的元素的 话, dataframe.at 和 dataframe.iat 是最快的方式,它们分别用于使用索引 和下标进行访问:

print(df.iat[2, 3])print(df.at['gamma', 'd'])8.08.0

四、进阶:Pandas 数据操作

掌握本章操作之后,基本可以处理大多数的数据了。为了看数据方便一些,我们设置一下输出屏幕的宽度

pd.set_option('display.width', 200)

4.1 数据创建的其它方式

数据结构的创建不止是之前介绍的标准形式,例如,我们可以创建一个以日期为元素的 Series :

dates = pd.date_range('20150101', periods=5)print(dates)DatetimeIndex(['2015-01-01', '2015-01-02', '2015-01-03', '2015-01-04',               '2015-01-05'],              dtype='datetime64[ns]', freq='D')

将这个日期 Series 作为索引赋给一个 DataFrame :

df = pd.DataFrame(np.random.randn(5, 4),index=dates,columns=list('ABCD'))print(df)                   A         B         C         D2015-01-01  0.008608 -0.686443 -0.021788  0.4344862015-01-02  0.711034  0.746027  1.528270  0.5572102015-01-03 -0.334801  0.532736  1.006003  0.0303722015-01-04  0.507740  0.668962 -0.166262  0.5183842015-01-05  0.887693 -0.839035  0.998530  1.066598

只要是能转换成 Series 的对象,都可以用于创建 DataFrame :

df2 = pd.DataFrame({ 'A' : 1., 'B': pd.Timestamp('20150214'), 'C': pd.Series(1.6,index=list(range(4)),dtype='float64'), 'D' : np.array([4] * 4, dtype='int64'), 'E' : 'hello pandas!' })print(df2)     A          B    C  D              E0  1.0 2015-02-14  1.6  4  hello pandas!1  1.0 2015-02-14  1.6  4  hello pandas!2  1.0 2015-02-14  1.6  4  hello pandas!3  1.0 2015-02-14  1.6  4  hello pandas!

4.2 数据的查看和排序

通过以下数据创建 DataFrame :

raw_data = [['000001.XSHE', '2015-01-05', '平安银行', 15.99, 16.28, 15.60, 16.02, 286043643], ['601998.XSHG', '2015-01-28', '中信银行', 7.04, 7.32, 6.95, 7.15, 163146128], ['000001.XSHE', '2015-01-07', '平安银行', 15.56, 15.83, 15.30, 15.48, 170012067], ['000001.XSHE', '2015-01-08', '平安银行', 15.50, 15.57, 14.90, 14.96, 140771421], ['000001.XSHE', '2015-01-09', '平安银行', 14.90, 15.87, 14.71, 15.08, 250850023], ['601998.XSHG', '2015-01-29', '中信银行', 6.97, 7.05, 6.90, 7.01, 93003445], ['000001.XSHE', '2015-01-06', '平安银行', 15.85, 16.39, 15.55, 15.78, 216642140], ['601998.XSHG', '2015-01-30', '中信银行', 7.10, 7.14, 6.92, 6.95, 68146718]]columns = ['secID', 'tradeDate', 'secShortName', 'openPrice', 'highestPrice', 'lowestPrice', 'closePrice', 'turnoverVol']df = DataFrame(raw_data, columns=columns)print(df)         secID   tradeDate secShortName  openPrice  highestPrice  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行      15.99         16.28        15.60       16.02    2860436431  601998.XSHG  2015-01-28         中信银行       7.04          7.32         6.95        7.15    1631461282  000001.XSHE  2015-01-07         平安银行      15.56         15.83        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行      15.50         15.57        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行      14.90         15.87        14.71       15.08    2508500235  601998.XSHG  2015-01-29         中信银行       6.97          7.05         6.90        7.01     930034456  000001.XSHE  2015-01-06         平安银行      15.85         16.39        15.55       15.78    2166421407  601998.XSHG  2015-01-30         中信银行       7.10          7.14         6.92        6.95     68146718[8 rows x 8 columns]

以上代码是2015年一月份两支股票的日行情信息,首先我们来 看一下数据的大小:

print(df.shape)(8, 8)

我们可以看到有8行,表示我们获取到了8条记录,每条记录有8个字段,现在预览一下数据, dataframe.head() 和 dataframe.tail() 可以查看数据的头五行和尾五行,若需要改变行数,可在括号内指定:

print("Head of this DataFrame:")print(df.head())print("Tail of this DataFrame:")print(df.tail(3))Head of this DataFrame:         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436431  601998.XSHG  2015-01-28         中信银行  ...         6.95        7.15    1631461282  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    250850023[5 rows x 8 columns]Tail of this DataFrame:         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol5  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034456  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    2166421407  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[3 rows x 8 columns]

dataframe.describe() 提供了 DataFrame 中纯数值数据的统计信息:

print(df.describe())       openPrice  highestPrice  lowestPrice  closePrice   turnoverVolcount   8.000000      8.000000     8.000000     8.00000  8.000000e+00mean   12.363750     12.681250    12.103750    12.30375  1.735769e+08std     4.422833      4.571541     4.300156     4.37516  7.490931e+07min     6.970000      7.050000     6.900000     6.95000  6.814672e+0725%     7.085000      7.275000     6.942500     7.11500  1.288294e+0850%    15.200000     15.700000    14.805000    15.02000  1.665791e+0875%    15.632500     15.972500    15.362500    15.55500  2.251941e+08max    15.990000     16.390000    15.600000    16.02000  2.860436e+08

对数据的排序将便利我们观察数据, DataFrame 提供了两种形式的排序。一种是按行列排序,即按照索引(行名)或者列名进行排序,可调用 dataframe.sort_index ,指定 axis=0 表示按索引(行名)排序, axis=1 表示按列名排序,并可指定升序(ascending=True)或者降序(ascending=False):

print("Order by column names, descending:")print(df.sort_index(axis=0, ascending=False).head())Order by column names, descending:         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol7  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     681467186  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    2166421405  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034454  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500233  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    140771421

第二种排序是按值排序,可指定列名和排序方式,默认的是升序排序:

print("Order by column value, ascending:")print(df.sort_values(by=['tradeDate']))print("Order by multiple columns value:")df = df.sort_values(by=['tradeDate', 'secID'], ascending=[False, True])print(df.head())Order by column value, ascending:         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436436  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    2166421402  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500231  601998.XSHG  2015-01-28         中信银行  ...         6.95        7.15    1631461285  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034457  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[8 rows x 8 columns]Order by multiple columns value:         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol7  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     681467185  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034451  601998.XSHG  2015-01-28         中信银行  ...         6.95        7.15    1631461284  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500233  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    140771421[5 rows x 8 columns]

4.3 数据的访问和操作

4.3.1 数据的筛选

已经介绍了使用 loc 、 iloc 、 at 、 iat 以及 [] 访 问 DataFrame 数据的几种方式,这里再介绍一种方法,使用 : 来获取部行或者全部列:

print(df.iloc[1:4][:])         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol1  601998.XSHG  2015-01-28         中信银行  ...         6.95        7.15    1631461282  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    140771421

我们可以扩展之前介绍的使用布尔类型的向量获取数据的方法,可以很方便地过滤数据,例如,我们要选出收盘价在均值以上的数据:

print(df[df.closePrice > df.closePrice.mean()].head())         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436432  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500236  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    216642140[5 rows x 8 columns]

isin() 函数可方便地过滤 DataFrame 中的数据:

print(df[df['secID'].isin(['601998.XSHG'])].head())         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol1  601998.XSHG  2015-01-28         中信银行  ...         6.95        7.15    1631461285  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034457  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[3 rows x 8 columns]

4.3.2 处理缺失数据

在访问数据的基础上,我们可以更改数据,例如,修改某些元素为缺失值:

df.loc[df['secID'] == '000001.XSHE', 'closePrice'] = np.nanprint(df)         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60         NaN    2860436431  601998.XSHG  2015-01-28         中信银行  ...         6.95        7.15    1631461282  000001.XSHE  2015-01-07         平安银行  ...        15.30         NaN    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90         NaN    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71         NaN    2508500235  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034456  000001.XSHE  2015-01-06         平安银行  ...        15.55         NaN    2166421407  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[8 rows x 8 columns]

查找元素缺失的行

print(df[df['closePrice'].isnull()])         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60         NaN    2860436432  000001.XSHE  2015-01-07         平安银行  ...        15.30         NaN    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90         NaN    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71         NaN    2508500236  000001.XSHE  2015-01-06         平安银行  ...        15.55         NaN    216642140[5 rows x 8 columns]

原始数据的中很可能存在一些数据的缺失,就如同现在处理的这个样例数据一样, 处理缺失数据有多种方式。通常使用 dataframe.dropna() , dataframe.dropna() 可以按行丢弃带有 nan 的数 据;若指定 how=‘all’ (默认是 ‘any’ ),则只在整行全部是 nan 时丢弃数 据;若指定 thresh ,则表示当某行数据非缺失列数超过指定数值时才保留;要指定根据某列丢弃可以通过 subset 完成。

# 使第一行的 closePrice 为 nandf.loc[1, 'closePrice'] = np.nan# 查看 df 过滤前的大小print("Data size before filtering:")print(df.shape)# 过滤所有含有 nan 的行print("Drop all rows that have any NaN values:")print("Data size after filtering:")print(df.dropna().shape)print(df.dropna())# 仅过滤整行为 nan 的行print("Drop only if all columns are NaN:")print("Data size after filtering:")print(df.dropna(how='all').shape)print(df.dropna(how='all'))# 过滤行当 nan 超过6个时print("Drop rows who do not have at least six values that are not NaN")print("Data size after filtering:")print(df.dropna(thresh=6).shape)print(df.dropna(thresh=6))# 当某一列的值为 nan 时才过滤该行print("Drop only if NaN in specific column:")print("Data size after filtering:")print(df.dropna(subset=['closePrice']).shape)print(df.dropna(subset=['closePrice']))Data size before filtering:(8, 8)Drop all rows that have any NaN values:Data size after filtering:(7, 8)         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436432  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500235  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034456  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    2166421407  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[7 rows x 8 columns]Drop only if all columns are NaN:Data size after filtering:(8, 8)         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436431  601998.XSHG  2015-01-28         中信银行  ...         6.95         NaN    1631461282  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500235  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034456  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    2166421407  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[8 rows x 8 columns]Drop rows who do not have at least six values that are not NaNData size after filtering:(8, 8)         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436431  601998.XSHG  2015-01-28         中信银行  ...         6.95         NaN    1631461282  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500235  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034456  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    2166421407  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[8 rows x 8 columns]Drop only if NaN in specific column:Data size after filtering:(7, 8)         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436432  000001.XSHE  2015-01-07         平安银行  ...        15.30       15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90       14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500235  601998.XSHG  2015-01-29         中信银行  ...         6.90        7.01     930034456  000001.XSHE  2015-01-06         平安银行  ...        15.55       15.78    2166421407  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[7 rows x 8 columns]

有数据缺失时也未必是全部丢弃, dataframe.fillna(value=value) 可以指定填补缺失值的数值

print(df.fillna(value=20150101).head())         secID   tradeDate secShortName  ...  lowestPrice   closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60        16.02    2860436431  601998.XSHG  2015-01-28         中信银行  ...         6.95  20150101.00    1631461282  000001.XSHE  2015-01-07         平安银行  ...        15.30        15.48    1700120673  000001.XSHE  2015-01-08         平安银行  ...        14.90        14.96    1407714214  000001.XSHE  2015-01-09         平安银行  ...        14.71        15.08    250850023[5 rows x 8 columns]

4.3.3 数据操作

Series 和 DataFrame 的类函数提供了一些函数,如 mean() 、 sum() 等,指定0按列进行,指定1按行进行:

print(df.mean(0))openPrice       1.236375e+01highestPrice    1.268125e+01lowestPrice     1.210375e+01closePrice      1.230375e+01turnoverVol     1.735769e+08dtype: float64

value_counts 函数可以方便地统计频数:

print(df['closePrice'].value_counts().head())16.02    17.15     115.48    114.96    115.08    1Name: closePrice, dtype: int64

在 panda 中, Series 可以调用 map 函数来对每个元素应用一个函数, DataFrame 可以调用 apply 函数对每一列(行)应用一个函 数, applymap 对每个元素应用一个函数。这里面的函数可以是用户自定义的一个 lambda函数,也可以是已有的其他函数。下例展示了将收盘价调整到 [0, 1] 区 间:

print(df[['closePrice']].apply(lambda x: (x - x.min()) / (x.max() - x.min())).head())   closePrice0    1.0000001    0.0220512    0.9404633    0.8831314    0.896362

使用 append 可以在 Series 后添加元素,以及在 DataFrame 尾部添加一行:

dat1 = df[['secID', 'tradeDate', 'closePrice']].head()dat2 = df[['secID', 'tradeDate', 'closePrice']].iloc[2]print("Before appending:")print(dat1)dat = dat1.append(dat2, ignore_index=True)print("After appending:")print(dat)Before appending:         secID   tradeDate  closePrice0  000001.XSHE  2015-01-05       16.021  601998.XSHG  2015-01-28        7.152  000001.XSHE  2015-01-07       15.483  000001.XSHE  2015-01-08       14.964  000001.XSHE  2015-01-09       15.08After appending:         secID   tradeDate  closePrice0  000001.XSHE  2015-01-05       16.021  601998.XSHG  2015-01-28        7.152  000001.XSHE  2015-01-07       15.483  000001.XSHE  2015-01-08       14.964  000001.XSHE  2015-01-09       15.085  000001.XSHE  2015-01-07       15.48

DataFrame 可以像在SQL中一样进行合并,在上篇中,我们介绍了使 用 concat 函数创建 DataFrame ,这就是一种合并的方式。另外一种方式使 用 merge 函数,需要指定依照哪些列进行合并,下例展示了如何根据security ID 和交易日合并数据:

dat1 = df[['secID', 'tradeDate', 'closePrice']]dat2 = df[['secID', 'tradeDate', 'turnoverVol']]dat = dat1.merge(dat2, on=['secID', 'tradeDate'])print("The first DataFrame:")print(dat1.head())print("The second DataFrame:")print(dat2.head())print("Merged DataFrame:")print(dat.head())The first DataFrame:         secID   tradeDate  closePrice0  000001.XSHE  2015-01-05       16.021  601998.XSHG  2015-01-28        7.152  000001.XSHE  2015-01-07       15.483  000001.XSHE  2015-01-08       14.964  000001.XSHE  2015-01-09       15.08The second DataFrame:         secID   tradeDate  turnoverVol0  000001.XSHE  2015-01-05    2860436431  601998.XSHG  2015-01-28    1631461282  000001.XSHE  2015-01-07    1700120673  000001.XSHE  2015-01-08    1407714214  000001.XSHE  2015-01-09    250850023Merged DataFrame:         secID   tradeDate  closePrice  turnoverVol0  000001.XSHE  2015-01-05       16.02    2860436431  601998.XSHG  2015-01-28        7.15    1631461282  000001.XSHE  2015-01-07       15.48    1700120673  000001.XSHE  2015-01-08       14.96    1407714214  000001.XSHE  2015-01-09       15.08    250850023
参数作用
left拼接的左侧DataFrame对象
right拼接的右侧DataFrame对象
on要加入的列或索引级别名称。 必须在左侧和右侧DataFrame对象中找到。 如果未传递且left_index和right_index为False,则DataFrame中的列的交集将被推断为连接键
left_on左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组
right_on左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组
left_index如果为True,则使用左侧DataFrame中的索引(行标签)作为其连接键。 对于具有MultiIndex(分层)的DataFrame,级别数必须与右侧DataFrame中的连接键数相匹配
right_index与left_index功能相似
how值有‘left’,‘right’,‘outer’,‘inner’,默认inner。inner是取交集,outer取并集,left保留左边所有行列数据并使用右边的数据进行补全,right与left相反
sort按字典顺序通过连接键对结果DataFrame进行排序。 默认为True,设置为False将在很多情况下显着提高性能
suffixes用于重叠列的字符串后缀元组。 默认为(‘x’,’ y’)
copy始终从传递的DataFrame对象复制数据(默认为True),即使不需要重建索引也是如此

DataFrame 另一个强大的函数是 groupby ,可以十分方便地对数据分组处理, 我们对2015年一月内十支股票的开盘价,最高价,最低价,收盘价和成交量求平均 值:

df_grp = df.groupby('secID')grp_mean = df_grp.mean()print(grp_mean)             openPrice  highestPrice  lowestPrice  closePrice   turnoverVolsecID                                                                      000001.XSHE  15.560000        15.988    15.212000   15.464000  2.128639e+08601998.XSHG   7.036667         7.170     6.923333    7.036667  1.080988e+08

如果希望取每只股票的最新数据,应该怎么操作呢? drop_duplicates 可以实现这个功能,首先对数据按日期排序,再按security ID去重:

df2 = df.sort_values(by=['secID', 'tradeDate'], ascending=[True, False])print(df2.drop_duplicates(subset='secID'))         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol4  000001.XSHE  2015-01-09         平安银行  ...        14.71       15.08    2508500237  601998.XSHG  2015-01-30         中信银行  ...         6.92        6.95     68146718[2 rows x 8 columns]

若想要保留最老的数据,可以在降序排列后取最后一个记录,通过指定 keep=‘last’ (默认取第一条记录)可以实现:

print(df2.drop_duplicates(subset='secID', keep='last'))         secID   tradeDate secShortName  ...  lowestPrice  closePrice  turnoverVol0  000001.XSHE  2015-01-05         平安银行  ...        15.60       16.02    2860436431  601998.XSHG  2015-01-28         中信银行  ...         6.95        7.15    163146128[2 rows x 8 columns]

4.3.4 数据可视化

pandas 数据直接可以绘图查看,下例中我们采用中国石化一月的收盘价进行绘 图,其中 set_index(‘tradeDate’)[‘closePrice’] 表示 将 DataFrame 的 ‘tradeDate’ 这一列作为索引,将 ‘closePrice’ 这一列作 为 Series 的值,返回一个 Series 对象,随后调用 plot 函数绘图,更多的参 数可以在 matplotlib 的文档中查看。

dat = df[df['secID'] == '600028.XSHG'].set_index('tradeDate')['closePrice']dat.plot(title="Close Price of SINOPEC (600028) during Jan, 2015")

五、代码简介

类型描述代码注释
数据创建创建 DataFramedf = DataFrame({'one': [1, 2, 3], 'two': ['a', 'b', 'c']}, index=['a', 'b', 'c'])index:行名
columns:列名
df = DataFrame([[1, 2, 3], ['a', 'b', 'c']], columns=['one', 'two', 'three'])
创建日期 Seriespd.date_range('20210101', periods=5)periods:递增数量
拼接 Seriesdf = pd.concat([Series([1, 2, 3]), Series(['a', 'b', 'c'])], axis=1)asxi:等于1时横向拼接,等于0时纵向拼接
数据基础访问查看数据大小df.shape-
查看前n行df.head(n)默认为5
查看后n行df.tail(n)
查看纯数值数据的统计信息df.describe()包括计数,平均值,标准差,最小值,最大值
访问列df['one'][]:访问列'one',类型为Series
df[['one']]
df[['one', 'two']]
[[]]:访问列'one',类型为DataFrame
访问行df.iloc[0]
df.iloc[0:2]
iloc:按下标选取行
loc:按行名选取行
注:行列括一起选取的是值,分开选取的是DataFrame
df.loc['a']
访问行+列df.loc[['a', 'b']][['one']]
df.iloc[0:2][['one']]
访问值df.iloc[0, 0]
df.loc['a', 'one']
数据筛选选取符合等式的行df[df['one'] > 1]选取列'one'的值大于1的行
选取含有某字符串的行df[df['two'].str.contains('a')]选取列'two'含有字符串'a'的行
选取属于某几个值的行df[df['one'].isin([1, 2])]选取列'one'的值属于[1, 2]的行
选取缺失行df[df['one'].isnull()]选取列'one’值缺失的行
选取非缺失行df[df['one'].notnull()]选取列'one’值非缺失的行
数据统计求均值df.mean(0)指定0按列进行,指定1按行进行
求和df.sum(0)
统计频数df['one'].value_counts()列'one'中每个值出现的次数
数据操作按列(行)名排序df.sort_index(axis=1, ascending=True)axis:等于1时按列名排序,等于0时按行名排序
ascending:为True时升序,为False时降序
按数值排序df.sort_values(by=['one', 'two'], ascending=[True, True])by:排序的列名
数据去重df.drop_duplicates(subset=['one'])根据列'one'去重,仅保留第一次出现的行
数据合并df.merge(df2, on=['one'])on:作为合并键值的列名
how:值有‘left’,‘right’,‘outer’,‘inner’,默认inner。inner是取交集,outer取并集,left保留左边所有行列数据并使用右边的数据进行补全,right与left相反
suffixes:用于重叠列的列名重命名,默认为(‘_x’,‘_y’)
对每列(行)应用一个函数df[['one']].apply(lambda x: x+1)lambda:自定义函数
对每个元素应用一个函数df[['one']].applymap(lambda x: x+1)
添加数据df.append(df2, ignore_index=True)ignore_index:为True时将行名重定义为有序数字,为False将原行名拼接
删除列df.drop('one', axis=1)-
数据分组df.groupby('one')根据列'one'对数据进行分组,分组后是一个DataFrameGroupBy对象,如想查看具体内容,可使用list转化为列表
将所有元素转化为字符串df.astype(str)-
将列元素转化为字符串df['one'].astype(str)-
将列元素根据逗号拆分df.drop('two', axis=1).join(df['two'].str.split(',', expand=True).stack().reset_index(level=1, drop=True).rename('tag'))rename:拆分后的列名

六、Pandas 与 Excel 的交互

5.1 读取 Excel 数据

将 Excel 表中的数据解析为 DataFrame

import pandas as pdwith pd.ExcelFile('saw.xlsx') as xlsx:	# 列出表名	names = xlsx.sheet_names	# 读取表 Sheet1 的数据	df = pd.read_excel(xlsx, 'Sheet1', na_value=['NA'], keep_default_na=False)    # na_value:把值解释为 nan    # keep_default_na:把空字符串解析为空字符串'',默认为True时解析为 nan    # 另外 index_col=0 可以把第一列设置为索引

5.2 写入 Excel 数据

将数据 DataFrame 写入到 Excel 表中

import pandas as pdfrom openpyxl import load_workbookwb = load_workbook('saw.xlsx')with pd.ExcelWriter('saw.xlsx', engine='openpyxl') as xlsx:    # 将数据 df 写入到表格 Sheet2 中    xlsx.book = wb    # 不保留索引和列名    df.to_excel(xlsx, sheet_name='Sheet2', index=False, header=None)

注:直接调用 ExcelWriter 方法写入的话,原有的表会被覆盖,所以需要结合 openpyxl 模块进行写入

七、参考文章

《Python 股票量化交易教程》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值