首页 > 技术文章 > Pandas模块

Treelight 2020-02-19 16:12 原文

Pandas简介

pandas是用于对数据进行分析,其需要依赖numpy模块,所以需要首先安装numpy 安装:pip install pandas 导入:import pandas as pd 主要功能: 1、具备对其功能的数据结构DataFrame、Series(即两种对象,其实有更多) 2、集成时间序列功能 3、提供丰富的数学运算和操作 4、灵活处理缺失数据

Series对象

Series对象是一种类似于一维数组的对象,由一组数据和一组与之相关的数据标签(索引)组成,好像是字典和列表的结合体

创建

方式一、pd.Series(列表或数组{,index=[列表]})

sr = pd.Series([1, 3, 5, 7, 9])
"""
0    1
1    3
2    5
3    7
4    9
""" 
sr = pd.Series([1, 3, 5, 7, 9], index=list('abcde'))
"""
a    1
b    3
c    5
d    7
e    9
dtype: int64
"""

方式二、pd.Series(字典)

sr = pd.Series({'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 9})
"""
a    1
b    3
c    5
d    7
e    9
dtype: int64
"""

取对应下标或标签的值

sr = pd.Series({'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 9})
"""
a    1
b    3
c    5
d    7
e    9
dtype: int64
"""
# 可按下标取值
print(sr[0])  # 1
# 如指定了标签,则可使用标签,字典的key
print(sr['c'])  # 5

属性

index属性和values属性
# index属性
sr = pd.Series({'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 9})
print(sr.index)  # Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
# values属性
sr = pd.Series({'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 9})
print(sr.values)  # [1 3 5 7 9]

iloc属性:指定用下标索引
loc属性:标签索引

Series特性

数组的特性

Series支持数组(numpy)的特性,如与数值的计算、两个Series的运算,索引、切片、通用函数(如np.sqrt(sr))、布尔值过滤、统计函数

字典的特性

1、in运算: 'a' in sr:判断a是不是sr的标签之一 for x in sr:这里注意,python循环的是字典的键,而在Series中循环的是sr的值
sr = pd.Series({'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 9})
for x in sr:
    print(x)
"""
1
3
5
7
9
"""
# 如果要循环标签,可加上index,如下:
for x in sr.index:
    print(x)
"""
a
b
c
d
e
"""

2、键索引:sr['a']、sr[['a', 'b', 'e']]
3、键切片:sr['a':'c'],注意python字典切片是不包尾,但在Series切片是包尾的

sr = pd.Series({'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 9})
print(sr['a': 'c'])
"""
a    1
b    3
c    5
"""

4、get函数:与字典的get是类似的sr.get('a', default=0)
5、shift(n):如果n是整数,则所有数据往下移,否则往上移

整数索引

以下情况:
sr1 = pd.Series([1, 3, 5, 7, 9, 11])
sr2 = sr1[2:5]
"""
2    5
3    7
4    9
"""

那么sr2[2]是按下标索引还是标签索引?下面做了个测试

print(sr2[2])  # 5

得出了是按标签索引,所以默认是按标签索引,而如果要指定是按下标索引怎么办呢?用到iloc属性,如下:

print(sr2.iloc[2])  # 9

也可指定用标签索引,用loc属性

print(sr2.loc[2])  # 5

数据对齐

Series之间进行运算,是根据相同标签查找的值进行运算,如下:
# 按标签进行运算,不是按下标运算
sr1 = pd.Series([12, 23, 34], index=list('cad'))
sr2 = pd.Series([11, 20, 10], index=list('dca'))
sr3 = sr1 + sr2
"""
a    33
c    32
d    45
"""

如果找不到对应的下标,则会用NaN标记,如下:

sr1 = pd.Series([12, 23, 34], index=list('cae'))
sr2 = pd.Series([11, 20, 10], index=list('dca'))
sr3 = sr1 + sr2
"""
a    33.0
c    32.0
d     NaN
e     NaN
"""

如果需要在缺失的数据上填上默认值怎么办呢?那这时候进行运算时就需要用到add、sub、div、mul等运算的fill_value,例子如下:

sr1 = pd.Series([12, 23, 34], index=list('cae'))
sr2 = pd.Series([11, 20, 10], index=list('dca'))
print(sr1.add(sr2, fill_value=0))
"""
a    33.0
c    32.0
d    11.0
e    34.0
"""

缺失数据的处理

1、sr.dropno():把Series中标记为NaN的数据丢弃
sr1 = pd.Series([12, 23, 34], index=list('cae'))
sr2 = pd.Series([11, 20, 10], index=list('dca'))
sr3 = sr1 + sr2
print(sr3.dropna())
"""
a    33.0
c    32.0
"""

2、sr.fillna(值):填充一个值
3、sr.isnull():返回一个布尔数组,缺失值对应为True

sr1 = pd.Series([12, 23, 34], index=list('cae'))
sr2 = pd.Series([11, 20, 10], index=list('dca'))
sr3 = sr1 + sr2
"""
a    False
c    False
d     True
e     True
"""

4、sr.notnull():返回一个布尔数值,缺失值对应为False
注意:panads一般需要重新赋值才可以继续使用,不赋值那还是原来的值???

DataFrame

DataFrame是一个表格型的数据结构,含有一组有序的列,可以被看做是由Series组成的字典,并且共用一个索引

创建方法

pd.DataFrame(字典),注意字典的值可以是一个列表,也可以是一个Series对象,例子如下
df = pd.DataFrame({'one': [1, 2, 3, 4], 'two': [4, 3, 2, 1]})
df = pd.DataFrame({'one': pd.Series([1, 2, 3], index=list('abc')),
                   'two': pd.Series(data=[1, 2, 3, 4], index=list('bacd'))})
print(df)

但DataFrame一般是由文件读取的:
pd.read_csv:默认分隔符为逗号
pd.read_table:默认分隔符为\t
pd.read_excel:读取excel文件
有以下参数
sep:指定分隔符,可用正则表达式如'\S+'
header=None:指定文件是否有列名
names:指定列名
index_col:指定某列作为索引
skip_row:指定路过某些行
na_values:指定某此字符串用NaN表达
parse_dates:指定某些列是否被解析为日期

写入文件:df.to_csv()
参数如下:
sep:指定分隔符
na_rep:指定缺失值转换的字符串,默认为空字符串
header=False:是否保存列名
index=False:不输出行索引
cols:指定输出的列到文件

了支持其它文件类型,如:json XML HTML 数据库
也能转换为二进制文件格式(pickle):
save
load

查看数据的属性与方法

index:获取索引 T:转置 columns:获取列索引 values:获取值数组 describe():获取快速统计 rename(columns={旧列名:新列名})

索引和切片

一、DataFrame有行索引和列索引 二、同样可以通过标签和下标两种方法进行索引和切片 三、使用索引和切片的方法 方法1、两个中括号,先取列再列行,df['A'][0] # A列的第0个行索引 方法2(推荐):使用loc/iloc属性,一个中括号,逗号隔开,先取行再列列。df.loc[0,'A'] 注意:向DataFrame对象中写入值时只使用方法2 标签获取: df['A']、df[['A','B']]、df['A'][0]、df.loc[:,['A','B']]、df.loc[:,'A':'C']、df.loc[0,'A']、df.loc[0:10, ['A','C']] 下标获取: df.iloc[3]、df.iloc[3,3]、df.iloc[0:3,4:6]、df.iloc[1:5,:] 布尔值过滤: df[df['A']>0]、df[df['A'].isin([1,3,5])]、df[df<0] = 0 四、行/列索引部分可以是常规索引、切片、布尔值索引、花式索引任意搭配。

数据对齐与缺失数据

DataFrame对象在运算时,同样会进行数据对齐,行索引与列索引分别对齐。结果的行索引与列索引分别为两个操作数的行索引与列索引的并集。 处理缺失数据的相关方法: dropna(axis=0,how='any') # axis为0时对列进行处理,1即对行处理,how为any时则其中一个为NaN则丢弃,all时则所有都为NaN才丢弃 fillna(值) isnull() notnull()

其他常用方法

mean(axis=0,skipna=False):求平均值 sum(axis=1):求和 sort_index(axis,...,ascending):索引排序,ascending默认为True,则升序 sort_values(by,axis,ascending):按by参数排序 NumPy的通用函数同样适用于pandas apply(func,axis=0):将自定义函数应用在各行或各列上,func可返回标量或Series
import pandas as pd

df = pd.read_csv('BHP1.csv')
# 计算High和Low的平均价
print(df.apply(lambda x: (x['High'] + x['Low']) / 2, axis=1))
# 计算high和low的平均值,返回Series
print(df.apply(lambda x: pd.Series([(x['High'] + x['Low']) / 2,
                                    (x['Open'] + x['Close']) / 2],
                                   index=['hl_mean', 'oc_mean']), axis=1))

applymap(func):将函数应用在DataFrame各个元素上

import pandas as pd

df = pd.read_csv('BHP1.csv')
# 将dataframe中的每个元素加1
print(df.applymap(lambda x: x + 1))

map(func):将函数应用在Series各个元素上 ,与上类似。

数据的分组与聚合

这个就相当于Excel中的分类汇总功能,但这个功能比起Excel的强大得多...... 分组:把某一列把数据拆分为若干组 聚合:组内应用某个函数

分组

分组的方法:DataFrame对象.groupby(by=None,...) 按一列分组:groupby('key1') 按多列分组:groupby(['key1', 'key2']) 按索引长度分组:groupby(len) 自定义分组:groupby(函数名) 获取分组信息: groups get_group('a') for name,group in df.groupby('key') 例子如下:
import pandas as pd

df = pd.DataFrame({'data1': [1, 1.2, 3.4, 5.7, 8], 'data2': [10.2, 5.6, 8.7, 3.5, 8.8],
                   'key1': list('abbab'), 'key2': list('fgfgf')})
"""
   data1  data2 key1 key2
0    1.0   10.2    a    f
1    1.2    5.6    b    g
2    3.4    8.7    b    f
3    5.7    3.5    a    g
4    8.0    8.8    b    f
"""
# 获取分组信息
# print(df.groupby('key1').groups)
# {'a': Int64Index([0, 3], dtype='int64'), 'b': Int64Index([1, 2, 4], dtype='int64')}
# print(df.groupby(['key1', 'key2']).groups)
# {('a', 'f'): Int64Index([0], dtype='int64'), ('a', 'g'): Int64Index([3], dtype='int64'), ('b', 'f'): Int64Index([2, 4], dtype='int64'), ('b', 'g'): Int64Index([1], dtype='int64')}
# 获取某一分组的值
# print(df.groupby('key1').get_group('a'))
# 获取多列分组的值
# print(df.groupby(['key1', 'key2']).get_group(('a', 'f')))
for name, group in df.groupby('key1'):
    print(name)
    print(group)
    print('-' * 50)

自定义分组:

import pandas as pd

df = pd.DataFrame({'data1': [1, 1.2, 3.4, 5.7, 8], 'data2': [10.2, -5.6, -8.7, 3.5, 8.8],
                   'key1': list('abbab'), 'key2': list('fgfgf'),
                   }, index=['Alex', 'Bob', 'Celina', 'Debi', 'Egon'])
# 按索引长度排序
# print(df.groupby(len).groups)
# 自定义分组,按data2中正数为一组,负数为一组,此处x可理解为索引
print(df.groupby(lambda x: 'zheng' if df['data2'][x] > 0 else 'fu').groups)

聚合

聚合:分组之后需要聚合函数来应用到每一组中。 内置聚合函数: max() min() count() mean() sum() 用法:DataFrame对象.groupby(字段名).聚合函数() 自定义聚合函数:agg() 有三种用法:自定义聚合、多个聚合函数、不同列应用不同聚合函数,例子如下:
import pandas as pd

df = pd.DataFrame({'data1': [1, 1.2, 3.4, 5.7, 8], 'data2': [10.2, -5.6, -8.7, 3.5, 8.8],
                   'key1': list('abbab'), 'key2': list('fgfgf'),
                   }, index=['Alex', 'Bob', 'Celina', 'Debi', 'Egon'])
"""
        data1  data2 key1 key2
Alex      1.0   10.2    a    f
Bob       1.2   -5.6    b    g
Celina    3.4   -8.7    b    f
Debi      5.7    3.5    a    g
Egon      8.0    8.8    b    f
"""
# 计算组内最大值和最小值之差
# print(df.groupby('key1').agg(lambda x: x.max() - x.min()))
# 多个聚合函数,计算组内最大值、最小值以及它们之间的差
# print(df.groupby('key1').agg(['max', 'min', lambda x: x.max() - x.min()]))
# 不同列应用不同的聚合函数,data1求最大值,data2求最小值
print(df.groupby('key1').agg({'data1': 'max', 'data2': 'min'}))

数据合并

数据合并分为两种:拼接(concatenate)、连接(join)

数据拼接:
pd.concat([df1,df2]) # 简单把数据拼接
pd.concat([df1,df2],keys=['a','b']) # 把数据表分层,然后拼接
pd.concat([df1,df2], axis=1) # 按行拼接
pd.concat([df1,df2],ignore_index=True)] # 索引要求是唯一
df1.append(df2)

数据连接:
pd.merged(df1,df2,on=字段)
pd.merged(df1,df2,on=[字段1,字段2]
pd.merged(df1,df2,on=字段,how='inner') # how参数可为inner outer left right

时间对象索引处理

这里首先大家要知道有一个可以用来灵活处理时间对象的包:dateutil,这个有什么好呢?不用指定时间格式,如下:
import dateutil

print(dateutil.parser.parse('2020-01-10'))
print(dateutil.parser.parse('2020/01/10'))
print(dateutil.parser.parse('2020-JAN-10'))
print(dateutil.parser.parse('JAN-10-2020'))
# 结果均为2020-01-10 00:00:00,是一个datetime格式

而pandas在此基础上又提供了成组处理时间对象

时间序列的创建

两种创建方法: pd.to_datetime(时间列表字符串):转换成DatetimeIndex pd.date_range,有以下参数: start:开始时间 end:结束时间 periods:时间长度 freq:时间频率,默认为'D',可先H(our) W(eek) Business S(emi-)M(onth) MinT(es) S(econd) A(year) 例如: pd.ta_datetime(start='2018-1-1', periods=10, freq='SM') # 频率为半个月 pd.ta_datetime(start='2018-1-1', periods=10, freq='W-FRI') # 频率为每周五
date_times = pd.to_datetime(['2019-12-12', '2020-02-10', '2020-01-30'])
print(date_times)
#  DatetimeIndex(['2019-12-12', '2020-02-10', '2020-01-30'], dtype='datetime64[ns]', freq=None)
print(date_times.to_pydatetime())  # 转换成python中的datetime对象
# 创建从2020-01-10到2020-01-20的时间索引
date_times1 = pd.date_range('2020-01-10', end='2020-01-20')
# 创建从2020-01-10开始的10天时间索引
date_times2 = pd.date_range('2020-01-10', periods=10)
# 创建从2020-01-10开始的10个月时间索引
date_times3 = pd.date_range('2020-01-10', periods=10, freq='M')

DatetimeIndex对象.to_pydatetime():转换成python中的datetime对象

时间序列的切片

时间序列就是以时间对象为索引的Series或DataFrame。datetime对象作为索引时是存储在DatetimeIndex对象中的。 时间序列的特殊功能: 传入“年”或“年月”作为切片方式,如 df['2018-2'] # 获取索引为2018-02月的记录 传入日期范围作为切片方式 df['2015':'2017'] 丰富的函数支持:strftime()、resample 如df.resample('3D').mean() # 以3天为一组,求平均值 df.resample('3D').first() # 取第一天的数据

推荐阅读