官方文档:https://pandas.pydata.org/
axis=0,表示纵向,行操作
axis=1,表示横向,列操作
pandas统计分析基础
1、读/写不同数据源的数据
在生产环境中,绝大多数数据都存储于数据库中。pandas提供了读取与存储关系型数据库数据的函数与方法。除了pandas库以外,还需要使用SQLAlchemy库建立对应的数据库连接。SQLAlchemy配合相应数据库的Python连接工具(如:MySQL需要pymysql库,Oracle需要cx_engine库),使用create_engine函数,建立一个数据库连接。pandas支持MySQL、postgresql、Oracle、SqlServer和SQLite等主流数据库。
以MySQL为例:
读写数据库数据
读取数据
pandas读取MySQL有3个函数,read_sql、read_sql_table、read_sql_query.
- read_sql_table只能读取数据库的一个表格,不能实现查询操作
- read_sql_query只能实现查询操作,不能直接读取数据库中的某个表
- read_sql既能读取数据库中的一个表、也能实现查询操作
import pandas as pd
pd.read_sql(
sql,
con,
index_col=None,
coerce_float=True,
params=None,
parse_dates=None,
columns=None,
chunksize=None,
)
read_sql_query(
sql,
con,
index_col=None,
coerce_float=True,
params=None,
parse_dates=None,
chunksize=None,
)
read_sql_table(
table_name,
con,
schema=None,
index_col=None,
coerce_float=True,
parse_dates=None,
columns=None,
chunksize=None,
)
SQLAlchemy连接MySQL数据库的代码:
from sqlalchemy import create_engine
#用户名为root,密码为1234
#地址为127.0.0.1,数据库名称为testdb,编码为utf-8
#数据库产品名+连接工具名://用户名:密码@数据库IP地址:数据库端口号/数据库名称?charset=数据库数据编码
engine = create_engine('mysql+pymysql://root:1234@127.0.0.1:3306/testdb?charset=utf-8')
重要参数说明:
参数名称 | 说明 |
---|---|
sql/table_name | 接收string。表示读取的数据的表名或者sql语句 |
con | 接收数据库连接。表示数据库链接信息 |
index_col | 表示设定的列作为列名,如果是一个数列,则是多重索引 |
coerce_float | 接收boolean。将数据库中的decimal类型转为pandas中的float64类型数据 |
columns | 接收list。表示读取数据的列名 |
使用函数读取数据库数据:
formlist = pd.read_sql_query('show tables',con=engine)
detail1 = pd.read_sql_table('table_name',con=engine)
detail2 = pd.read_sql('se;ect * from meal_order_detail2',con=engine)
存储数据
将DataFrame写入数据库中,也要依赖SQLAlchemy库的create_engine函数创建数据库连接。数据库存储只有一个方法:to_sql()
,其用法与常用参数
DateFrame.to_sql(
name,
con,
schema=None,
if_exists="fail",
index=True,
index_label=None,
chunksize=None,
dtype=None,
method=None,
)
参数名称 | 说明 |
---|---|
name | 接收string。代表数据库表名 |
con | 接收数据库连接 |
if_exists | 接收fail、replace、append。fail表示如果表名存在,则不执行写入操作;replace表示如果存在,则将原表替换掉;append则表示在源数据表的基础上追加数据。默认为fail |
index | 接收boolean。表示是否将行索引作为数据存入数据库。默认为True |
index_label | 接收string或者sequence。代表是否引用索引名称 |
dtype | 接收dict。代表写入的数据类型(列名为key,数据格式为values)。默认为None |
读写文本文件
读取数据
pandas提供了两种函数读取文本文件
read_table()
读取文本文件read_csv()
读取csv文件,即字符分隔文件
#详细见官方文档
pd.read_table(filepath,sep='\t',header='infer',names=None,index_col=None,dtype=None,encoding=utf-8,engine=None,nrows=None)
pd.read_csv(filepath,sep=',',header='infer',names=None,index_col=None,dtype=None,encoding=utf-8,engine=None,nrows=None)
参数名称 | 说明 |
---|---|
filepath | 接收string。代表文件路径 |
sep | 接收string。代表分隔符 |
header | 接收int或sequence。表示将某行数据作为列名。默认infer,表示自动识别 |
names | 接收array。表示列名 |
index_col | 接收int、sequence、False。表示索引列的位置 |
dtype | 接收dict。代表写入的数据类型 |
engine | 接收c或者python。代表数据解析引擎。默认为c |
nrows | 接收int。表示读取前几行 |
存储数据
对于结构化数据,通过to_csv()
函数实现以csv文件格式存储。
DataFrame.to_csv(
path_or_buf=None,
sep=",",
na_rep="",
float_format=None,
columns=None,
header=True,
index=True,
index_label=None,
mode="w",
encoding=None,
compression="infer",
quoting=None,
quotechar='"',
line_terminator=None,
chunksize=None,
date_format=None,
doublequote=True,
escapechar=None,
decimal=".",
)
常用参数说明:
参数名称 | 说明 |
---|---|
path_or_buf | 接收string。代表文件路径 |
sep | 接收string。代表分隔符,默认为’,’ |
na_rep | 接收string。代表缺失值,默认为"" |
columns | 接收list。代表写出的列名 |
header | 接收boolean。代表是否将列名写出。默认为True |
index | 接收boolean。表示索引名。代表是否将行名写出。默认为True |
index_label | 接收sequence。表示索引名 |
mode | 接收特定的string。代表数据写入模式。默认为w |
encoding | 接收特定的string。代表存储文件的编码格式 |
读写Excel文件
读取数据
import pandas as pd
pd.read_excel(io, sheet_name=0, header=0, names=None, index_col=None, usecols=None, squeeze=False,dtype=None, engine=None, converters=None, true_values=None, false_values=None, skiprows=None, nrows=None, na_values=None, parse_dates=False, date_parser=None, thousands=None, comment=None, skipfooter=0, convert_float=True, **kwds)
#读取csv文件时,使用encoding='gbk'解决中文问题
1、io,Excel的存储路径
2、sheet_name,要读取的工作表名称
3、header, 用哪一行作列名
4、names, 自定义最终的列名
5、index_col, 用作索引的列
6、usecols,需要读取哪些列
7、squeeze,当数据仅包含一列
8、converters ,强制规定列数据类型
9、skiprows,跳过特定行
10、nrows ,需要读取的行数
11、skipfooter , 跳过末尾n行
存储数据
DateFrame.to_excel(
excel_writer,
sheet_name="Sheet1",
na_rep="",
float_format=None,
columns=None,
header=True,
index=True,
index_label=None,
startrow=0,
startcol=0,
engine=None,
merge_cells=True,
encoding=None,
inf_rep="inf",
verbose=True,
freeze_panes=None,
)
with pd.ExcelWriter(r'{}\{}.xlsx'.format(path,'table_rank')) as writer:
table_rank.to_excel(excel_writer=writer,sheet_name='{}'.format('排名变化表'))
##2、掌握Series的常用操作
- Series 是一个带有 名称 和 索引 的一维数组。
- Series 中包含的数据类型可以是整数、浮点、字符串、列表、元组、ndarray等。
- 假定有一个场景是:存储一些用户的信息,暂时只包括年龄信息。
- 我们可以通过 Series 来存储,这里我们通过 Series 存储了四个年龄:18/30/25/40,只需将要存储的数据构建成一个数组,然后赋值给data参数即可。
import pandas as pd
import numpy as np
pd.Series(['data=None', 'index=None', 'dtype=None', 'name=None', 'copy=False', 'fastpath=False'],)
Series索引的添加与修改
方法1
name = ['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠', '蝙蝠侠', '索尔']
age = [22, 3000, 33, 37, 40, 1500]
heroes_age = pd.Series(age,index=name) #索引index作为pd.Series()中的参数来为heroes_age指定索引
heroes_age
heroes_age.index
方法2
建立好Series之后,用一个新的列表(或者其他有序序列)赋值到该Series的索引对象中。
user_age = pd.Series([22, 3000, 33, 37, 40, 1500])
user_age.index = ['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠', '蝙蝠侠', '索尔']
user_age
Series名字的添加和修改
索引名的添加
user_age.index = ['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠', '蝙蝠侠', '索尔']
user_age.index.name = '英雄姓名'
user_age
user_age.index.name
In [6]: user_age.index.name = '英雄姓名'
In [7]: user_age
Out[7]:
英雄姓名
蜘蛛侠 22
灭霸 3000
奇异博士 33
钢铁侠 37
蝙蝠侠 40
索尔 1500
dtype: int64
Series名字的添加
#直接用pd.Series()中的name参数来设置
name = ['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠', '蝙蝠侠', '索尔']
age = [22, 3000, 33, 37, 40, 1500]
heroes_age = pd.Series(age,index=name,name='英雄年龄')
heroes_age.name
#通过pd.Index方法先创建一个索引,再将索引添加到series中去
data=[22, 3000, 33, 37, 40, 1500]
index = pd.Index(['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠','蝙蝠侠', '索尔' ], name="英雄姓名") #注意这里的Index中的"i"是大写
user_age = pd.Series(data=data, index=index, name="英雄年龄")
user_age
##运行结果
英雄姓名
蜘蛛侠 22
灭霸 3000
奇异博士 33
钢铁侠 37
蝙蝠侠 40
索尔 1500
Name: 英雄年龄, dtype: int64
Series的索引和切片
import numpy as np
import pandas as pd
name = ['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠', '蝙蝠侠', '索尔']
age = [22, 3000, 33, 37, 40, 1500]
heroes_age = pd.Series(age,index=name)
##索引
heroes_age[0]
heroes_age['蜘蛛侠']
##切片
heroes_age[0::2]
heroes_age['蜘蛛侠':'索尔':2]
#单独抽取某些数据
heroes_age[[1,2,4]]
heroes_age[['灭霸','奇异博士','蝙蝠侠']]
##get方法索引
heroes_age["蜘蛛侠"]
heroes_age.get("蜘蛛侠")
heroes_age.get("闪电侠", '不存在')
掩码提取
因为Series底层封装的也ndarray数组结构, 因此同样支持向量化操作, 可以利用
import numpy as np
import pandas as pd
name = ['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠', '蝙蝠侠', '索尔']
age = [22, 3000, 33, 37, 40, 1500]
heroes_age = pd.Series(age,index=name)
heroes_age[heroes_age>100]
heroes_age[(heroes_age>1000)&(heroes_age<2000)]
# 提取年龄为偶数的数据
#提取年龄为偶数,且年龄小于100的英雄年龄
heroes_age[heroes_age%2==0]
heroes_age[(heroes_age%2==0)]
heroes_age[(heroes_age%2==0)&(heroes_age<100)] # 在多个逻辑条件下,用& 或者|
3、掌握DataFrame的常用操作
DataFrame 是一个带有索引的二维数据结构,每列可以有自己的名字,并且可以有不同的数据类型。你可以把它想象成一个 excel 表格或者数据库中的一张表,也可以将它想象成由多个Series拼接成的一个DataFrame,公用一个索引,它是最常用的 Pandas 对象。
DataFrame的创建
import numpy as np
import pandas as pd
pd.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
转化数组
a=np.arange(1,10).reshape(3,3)
print(pd.DataFrame(a))
通过字典转化
data = {
"年龄":[19, 3000, 30, 37, 40, 1500],
"出生地":["纽约皇后区", "泰坦星球","费城", "纽约", "哥谭", "阿斯加德" ]
}
#先将dataframe的索引创建,再将索引添加到dataframe中去
index = pd.Index(data=['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠','蝙蝠侠', '索尔' ], name="英雄姓名")
user_info = pd.DataFrame(data=data,index=index )
print(user_info)
通过多维数组转化
除了上面这种传入 dict 的方式构建外,我们还可以通过另外一种方式来构建。这种方式是先构建一个二维数组,然后再生成一个列名称列表。
data = [[19, "纽约皇后区"],
[3000, "泰坦星球"],
[30, "费城"],
[37, "纽约"],
[40, "哥谭"],
[1500, "阿斯加德"]]
columns = ["年龄", "出生地"]
index = pd.Index(data=['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠','蝙蝠侠', '索尔' ], name="英雄姓名")
user_info = pd.DataFrame(data=data, index=index, columns=columns)
print(user_info)
查看DataFrame的常用属性
DataFrame的基础属性:
- values:获取元素
- index:索引
- columns:列名
- dtypes:类型
- size:DataFrame元素个数
- ndim:DataFrame的维度数
- shape:DataFrame的数据形状
- T:实现DataFrame的数据转置(行列转换)
- 属性调用:
DataFrame.values
增删改查DataFrame数据
增
添加一列的方法:只需要新建一个列索引,然后对该索引进行数据赋值即可
np.where()的妙用
如果新列是依据原有的列生成的“衍生变量”,那么需要用到np.where(),比如要生成一列变量,将性别的“男”转化为“1”,“女”转化为“0”
import numpy as np
user_info
np.where(user_info.性别=="男",1,0) #这里返回的是numpy的ndarray
user_info["sex"]=np.where(user_info.性别=="男",1,0)
user_info
删
行列删除
删除某行或者某列的数据用pandas提供的drop方法:
DataFrame.drop(label,axis=0,index=None,columns=None,level=None,inplace=False,erros='raise')
参数名称 | 说明 |
---|---|
labels | 接收string或者array。代表删除的行或列的标签 |
axis | 接收0或1.代表操作的轴向,(0或’行’)或(1或’列’) |
levels | 接收int或索引名。代表标签所在级别。 |
inplace | 接收boolean。代表操作是否对原数据生效。默认为False |
列删除
user_infor2.pop("列名")
列顺序的更改
如果我们想新修改user_infor2的列顺序,有两个方法:
-
df[列名组]
先用列表定义行的列名顺序,再按照这个新的顺序重新赋值回原DataFrame:
hehe=["年龄","性别","出生地"] user_infor2[hehe] #这里只是返回新DataFrame,并没有修改原DataFrame
-
df.pop() + df.insert()
- 将要移动位置的列用DataFrame.pop()删除,弹出的该列series赋值到新变量。
- 然后用DataFrame.insert()将弹出来的这列指定位置插回原DataFrame。
- 这种做法当然会修改原DataFrame
sex=user_infor2.pop("性别") user_infor2.insert(1,"性别",sex) user_infor2
改
更改DataFrame中的数据原理是将这部分数据提取出来,重新复制为新数据,需要注意的是:数据更改直接对源数据进行更改,操作无法撤销。如果做出更改,需要对更改条件进行确认或对数据进行备份。
df.assign()
通过上面的例子可以看出,我们创建新的列的时候都是在原有的 DataFrame 上修改的,也就是说如果添加了新的一列之后,原有的 DataFrame 会发生改变。
我们可以通过 assign 方法来创建新的一列,并返回一个新DataFrame
,不修改原DataFrame。
user_info.assign(新列 = 88) #自然对数(e为底)
DataFrame.assign()往往会搭配np.where()使用:
比如,依据原有的user_infor表,生成一个新的DataFrame,新的DataFrame新增一列变量,如果英雄性别为男,年龄增加10岁,性别为女增加5岁,新列列名为“新年龄”。
查
-
DataFrame的基本查看方式
- 获取单列数据:
DataFrame['列名']
、DataFrame.属性名
- 获取单列多行数据:
DataFrame['列名'][:5]
- 获取多列多行数据:
DataFrame[['列名1','列名2']][:5]
- 获取多行数据:
DataFrame[:][:5]
- head()和tail()方法:
DataFrame.head(n)
、DataFrame.tail(n)
- 获取单列数据:
-
loc、iloc访问方式
-
DataFrame.loc[行索引名称会条件,列索引名称]
#条件切片 DataFrame.loc[DataFrame['列名']=='111','列名2']
-
DataFrame.iloc[行索引位置,列索引位置]
DataFrame.loc[(DataFrame['列名']=='111').values,[1,5]]
-
-
切片方法之ix:
DataFrame.ix[行索引名称或位置,列索引名称或位置]
ix方法既可以接收索引名称,也可以接收索引位置,需注意:当索引名称和位置存在部分重叠时,ix默认优先识别名称。
#ix索引位置为闭区间 DataFrame.ix[2:6,5]
4、索引和列名的修改
在使用 DataFrame 的过程中,经常会遇到修改列名,修改索引名、修改索引等情况。使用 rename 轻松可以实现。
- 修改索引名用需要在df.rename()中设置index参数。
- 修改列名只需要设置参数 columns。
修改索引名
user_infor2.index.name="heros names"
修改索引
要将整个索引替换掉,只需要建立一个字典:
- 该字典的“键”对应着“旧索引”
- 该字典的“值”对应着“新索引”
a=['蜘蛛侠', '灭霸', '奇异博士', '钢铁侠', '蝙蝠侠', '索尔']
b=["荷兰弟","乔什·布洛林","本尼迪克特·康伯巴奇","小罗伯特·唐尼","本·阿弗莱克","克里斯·海姆斯沃斯"]
c=dict(zip(a,b))
user_infor2=user_infor2.rename(index=c) #将行索引改为上面字典的映射关系
user_infor2.index.name="演员" #添加行索引名
user_infor2
修改列名
修改列名只需要设置参数 columns。
user_infor2.columns.name="角色属性"
c={"年龄":"Age","性别":"Sex","出生地":"Birthplace"}
user_infor2=user_infor2.rename(columns=c)
user_infor2
描述分析DataFrame数据
有时候我们获取到数据之后,想要查看下数据的简单统计指标(最大值、最小值、平均值、中位数等),比如想要查看年龄的最大值,如何实现呢?
DataFrame中有两种方法可以实现:
- 方法一:直接通过DataFrame抽取出来的单列series使用series的相应统计方法或函数。
- 方法二:通过使用numpy对应的方法或函数,处理目标就是DataFrame抽出来的series。
user_info.Age.mean()
np.mean(user_info.Age)
那么,series能用哪些描述性统计的指标呢?——numpy中ndarray相应的函数都能用在这里
#numpy的函数都能用在这里
user_infor.Age.max()
user_infor.Age.idxmax()
user_infor.Age.idxmin()
user_infor.loc[user_infor.Age.idxmax()]
类似的,通过调用 min、mean、quantile、sum 方法可以实现最小值、平均值、中位数以及求和。可以看到,对一个 Series 调用 这几个方法之后,返回的都只是一个聚合结果。
user_info.Age.cumsum()
###例题
grade=pd.read_csv('student_grade.txt',sep='\t')
grade.head()
grade["数学"].mean()
grade['数学'].std()
t=grade[['数学','语文','英语']]
grade['总分1']=np.sum(t,axis=1)
grade['总分2']=grade.英语+grade.数学+grade.语文
grade
grade['差值']=grade.数学-grade.语文
grade['差值']=grade.差值.abs()
grade.head(5)
pandas常用描述性统计方法
方法名称 | 说明 | 方法名称 | 说明 |
---|---|---|---|
min | 最小值 | max | 最大值 |
mean | 均值 | ptp | 极差 |
median | 中位数 | std | 标准差 |
var | 方差 | cov | 协方差 |
sem | 标准误差 | mode | 众数 |
skew | 样本偏度 | kurt | 样本峰度 |
quantile | 四分位数 | count | 非空数值数目 |
describe | 描述统计 | mad | 平均绝对离差 |
如果想要获取更多的统计方法,可以参见官方链接:Descriptive statistics (http://pandas.pydata.org/pandas-docs/stable/basics.html#descriptive-statistics)
虽然说常见的各种统计值都有对应的方法,如果我想要得到多个指标的话,就需要调用多次方法,是不是显得有点麻烦呢?
Pandas 设计者自然也考虑到了这个问题,想要一次性获取多个统计指标,只需调用 describe 方法即可
df.describe()
# 只支持数值列
grade.describe()
#直接调用 describe 方法后,会显示出数字类型的列的一些统计指标,如 总数、平均数、标准差、最小值、最大值、25%/50%/75% 分位数。
user_infor.describe(include=["object"]) #显示数据类型是object的字段信息
#结果展示了非数字类型的列的一些统计指标:总数,去重后的个数、最常见的值、最常见的值的频数。
Series.value_counts
频数统计:
user_infor['Birthplace'].value_counts()
如果想要获取某列最大值或最小值对应的索引,可以使用 idxmax 或 idxmin 方法完成。
user_infor.Age.idxmin() #注意区分,ndarray对应方法为np.argmin()
单列数据类型转换
如果想要转换数据类型的话,可以通过 astype 来完成。
Series.astype()
比如,想将年龄的数据类型从"int64"转为"float":
user_infor2.Age = user_infor2.Age.astype(float) #用dtype就要写成"float64",记得加双引号
user_infor2.info()
user_infor2
Series.to_numeric()
有时候会涉及到将 object 类型转为其他类型,常见的有转为数字、日期、时间差。 Pandas 中分别对应 to_numeric、to_datetime、to_timedelta 方法。 比如,如果我们给user_infor增加了一列新的字段“身高”
user_infor2["Height"] = ["175", "270", "178", "177", "185", "188", "168"]
#转换为数字
pd.to_numeric(user_infor2.Height)
user_infor2["Height"] = pd.to_numeric(user_infor2.Height)
##对身高字段进行统计描述
user_infor2["Height"].describe()
如果身高里面多了一些"cm"的字符,使用Series.to_numeric()会转换失败:
这时候可以通过Series.to_numeric()方法里面errors参数的设定来进行区分处理,errors可以设置参数’ignore’, ‘raise’, ‘coerce’:
- 如果’raise’,则无效的解析将引发异常。
- 如果’coerce’,则无效解析将被设置为NaN。
- 如果’ignore’,则无效的解析将返回输入。
user_infor2["Height"] = ["175", "270cm", "178cm", "177", "185", "188cm", "168"]
user_infor2["Height"] = pd.to_numeric(user_infor2.Height,errors="coerce")
user_infor2["Height"] = ["175", "270cm", "178cm", "177", "185", "188cm", "168"]
user_infor2["Height"] = pd.to_numeric(user_infor2.Height,errors="ignore")
user_infor2
5、转换与处理时间序列数据
转换字符串时间为标准时间
在多数情况下,对时间类型数据进行分析的前提就是将原本为字符串的时间转换为标准时间。pandas继承了NumPy库和datetime库的时间相关模块,提供了6中时间相关的类。
类名称 | 说明 |
---|---|
Timestamp | 最基础的时间类。表示某个时间点。绝大多数的场景中的时间数据都是Timestamp |
Period | 表示单个时间跨度,或者某个时间段,例如某一天、某一小时等 |
Timedelta | 表示不同单位的时间,例如1d、1.5h等,而非具体的某个时间段 |
DatetimeIndex | 一组Timestamp构成的index,可以用来作为Series或者DataFrame的索引 |
PeriodtimeIndex | 一组Period构成的index,可以用来作为Series或者DataFrame的索引 |
TimedeltaIndex | 一组Timedelta构成的index,可以用来作为Series或者DataFrame的索引 |
-
Timestamp:将与时间相关的字符串转为Timestamp。
pd.to_datetime()
-
还可以将原时间数据提取出来转换为DatetimeIndex 、PeriodtimeIndex
pd.DatetimeIndex()
、pd.PeriodIndex(Series,freq='S')
,转换为PeriodIndex的时候,需要通过freq参数指定时间间隔,常用的时间间隔有Y、M、D、H、T、S。两个函数可以用来转换数据,还可以用来创建时间序列数据。参数名称 说明 data 接收array。表示DatetimeIndex的值 freq 接收string。表示时间的间隔频率 start 接收string。表示生成规则时间数据的起点 periods 表示需要生成的周期数目 end 接收string。表示生成规则时间数据的终点 tz 接收timezone。表示数据的市区 name 接收int或string。指定DatimeIndex的名字 -
DatetimeIndex用来指代一系列时间点的一种数据结构
-
PeriodIndex用来指代一系列时间段的数据节后-
提取时间序列数据信息
在多数涉及与时间相关的数据处理、统计分析的过程中,都需要提取实践中的年份、月份等数据。
- 使用对应的Timestamp类属性就能够实现这一目的。
属性名称 | 说明 | 属性名称 | 说明 |
---|---|---|---|
year | 年 | week | 一年中第几周 |
month | 月 | quarter | 季节 |
day | 日 | weekofyear | 一年中第几周 |
hour | 小时 | dayofyear | 一年中第几天 |
minute | 分钟 | dayofweek | 一周第几天 |
second | 秒 | weekday | 一周第几天 |
date | 日期 | weekday_name | 星期名陈 |
time | 时间 | is_leap_year | 是否闰年 |
结合python列表推导式,可以实现对DataFrame某一列时间信息数据的提取
[i for i in DataFrame['lock_time']]
- 在DatetimeIndex和PeriodIndex中提取对应信息的方法更加简单,以类属性方法实现:
pd.DatetimeIndex().weekday_name
,需要注意的是:PeriodIndex相比于DatetimeIndex少了weekdar_name属性,所以不能用该属性提取星期名称,可以通过weekday属性,而后将06分别复制为MondaySunday
加减时间数据
-
Timedelta类是时间相关类中的一个异类,不仅能够使用正数,还能使用负数表示单位时间。
-
类的周期名称、对应单位及说明
周期名称 单位 说明 weeks 无 星期 days D 天 hours h 小时 minutes m 分 seconds s 秒 milliseconds ms 毫秒 microseconds us 微秒 nanoseconds ns 纳秒 #将时间数据向后平移一天 time1 = DataFrame['time'] + pd.Timedelta(days=1) #时间数据相减,减去2017年1月1日0点0时0分0秒 timedelta = DataFrame['time'] - pd.to_datetime('2017-1-1')
6、使用分组聚合进行组内运算
groupby拆分数据
groupby方法提供的是分组聚合步骤中的拆分功能,能够根据索引或者字段对数据进行分组。
DataFrame.groupby(
by=None,
axis=0,
level=None,
as_index=True,
sort=True,
group_keys=True,
squeeze=False,
observed=False,
**kwargs
)
参数名称 | 说明 |
---|---|
by | 接收llist、string、mapping、generator。用于确定分组的依据。如果传入的是函数,则对索引进行计算并分组;如果传入的是字典或Series,则字典或Series的值用来作为分组依据;如果传入一个NumPy数组,则数据的元素作为分组依据;如果传入的是字符串或者字符串列表,则使用这些字符串所代表的字段作为分组依据 |
axis | 接收int。表示操作的轴向,默认为0 |
level | 接收int或索引名。代表标签所在级别。默认为None |
as_index | 接收boolean。表示聚合后的聚合标签是否以DataFrame索引形式输出,默认为True |
sort | 接收boolean。表示是否对分组依据、分组标签进行排序、默认为True |
group_keys | 接收boolean。表示是否显示分组标签的名称,默认为True |
squeeze | 接收boolean。表示是否在允许的情况下对返回数据进行降维。默认为False |
-
分组后结果并不能直接查看,而后被存于内存中,输出的内存地址。是pandas提供的一种对象,Groupby对象常用的描述性统计方法及说明:
方法名称 说明 count 计算分组的数目,包括缺失值 head 返回每组的前n个值 max 返回每组的最大值 mean 返回每组的均值 median 返回每组的中位数 cumcount 对每个分组的组员进行标记,0~n-1 size 返回每组的大小 min 返回每组的最小值 std 返回每组的标准差 sum 返回每组的和 detailGroup = DataFrame[['列名1','列名2','列名3']].group(DataFrame['列名1']).mean() detailGroup = DataFrame.group(['列名1'])['列名1','列名2','列名3'].mean()
agg聚合数据
DataFrame.agg(func,axis=0,*args,**kwargs)
DataFrame.aggregate(func,axis=0,*args,**kwargs)
参数名称 | 说明 |
---|---|
func | 接收list、dict、function。表示应用于每行或每列的函数 |
axis | 接收0或1。代表操作的轴向,默认为0 |
#使用agg求出当前数据对应的统计量
DataFram.agg([np.sum,np.mean])
#分别求出一个字段的总和,另一个字段的均值
DataFrame.agg({'字段1':np.sum,'字段2':np.mean})
#agg里面也可以传入自定义函数
#agg方法对分组数据使用不同的聚合函数
DataFrame.groupby(by='列名').agg({'列名2':np.count,'列名3':np.sum})
apply聚合数据
DataFrame.apply(
func, #接收传入的函数
axis=0, #操作的轴向
broadcast=None, #是否进行广播
raw=False, #是否将ndarray对象传递给函数
reduce=None, #表示返回值的格式
result_type=None,
args=(),
**kwds
)
DataFrame.groupby(by='列名').apply(np.mean)
transform聚合数据
DataFrame.transform(func)
,可以对整个DataFrame进行操作
7、创建透视表与交叉表
使用pivot_table函数创建透视表
pd.pivot_table(
data, #接收DataFrame
values=None, #接收string。指定要聚合的数据字段名
index=None, #接收string、list。表示行分组键
columns=None, #接收string、list。表示列分组键
aggfunc="mean", #接收函数,表示聚合函数
fill_value=None, #填充缺失值
margins=False, #接收boolean。表示汇总开关
dropna=True, #是否删除权威NaN的列
margins_name="All",
observed=False,
)
DataFrame.pivot_table(
values=None,
index=None,
columns=None,
aggfunc="mean",
fill_value=None,
margins=False,
dropna=True,
margins_name="All",
observed=False,
)
使用crosstab函数创建交叉表
交叉表是一种特殊的透视表,主要用于计算分组频率。
pd.crosstab(
index, #接收string、list。表示行索引建
columns, #接收string、list。表示列索引建
values=None,#接收array,表示聚合数据
rownames=None,#表示行分组键名
colnames=None,#表示列分组键名
aggfunc=None, #表示聚合函数
margins=False,#汇总功能的开关,默认为True
margins_name="All",
dropna=True, #是否删除全为NaN的列,默认为False
normalize=False,#是否对值进行标准化。默认为False
)
使用pandas进行数据预处理
1、合并数据
堆叠合并数据
pd.concat
堆叠就是简单的把两个表拼在一起,也被称为轴向连接、绑定或连接。
pd.concat(
objs, #接收合并对象
axis=0, #0代表纵向合并
join="outer", #表示其他轴上的索引是按交集(inner)还是并集(outer)合并
join_axes=None, #接收Index对象,表示用于其他n-1条轴的索引
ignore_index=False, #是否不保留连接轴上的索引
keys=None,
levels=None,
names=None,
verify_integrity=False,
sort=None,
copy=True,
)
pd.concat([user_infor,user_infor],axis=0)
pd.concat([user_infor,user_infor],axis=1)
df.append(df)
- append方法可纵向合并数据,但是两张表的列名需要完全一致
DataFrame.append(other, ignore_index=False, verify_integrity=False, sort=None)
user_infor.append(user_infor)
主键合并数据
pd.merge(
left,
right,
how="inner", #连接方式
on=None,
left_on=None, #左表主键名
right_on=None, #右表主键名
left_index=False,
right_index=False,
sort=False,
suffixes=("_x", "_y"),
copy=True,
indicator=False,
validate=None,
)
pd.merge()
pd.merge('left', 'right', "how='inner'", 'on=None', 'left_on=None', 'right_on=None)
-
left:仅使用左框架中的键,类似于SQL左外连接;保留关键顺序
pd.merge(user_info_01,user_info_02,how="left",left_on="Hero Name",right_on="英雄名")
-
right:仅使用右框架中的键,类似于SQL右外连接;保留关键顺序
pd.merge(user_info_01,user_info_02,how="right",left_on="Hero Name",right_on="英雄名")
-
outer:使用来自两个帧的键的并集,类似于SQL full outer加入;按字典顺序排序键
-
inner:使用两个帧的交集,类似于SQL内部加入;保留左键的顺序
pd.merge(user_info_01,user_info_02,how="inner",left_on="Hero Name",right_on="英雄名")
-
new_infor=pd.merge(user_info_01,user_info_02,how="right",left_on="Hero Name",right_on="英雄名")
new_infor=new_infor.drop("英雄名",axis=1)
a={"演员":"Actor or Actress"}
new_infor=new_infor.rename(columns=a)
new_infor.iloc[7,:]=["黑寡妇",35,"女","斯大林格勒","枪械"]
new_infor["Age"]=new_infor["Age"].astype(int)
new_infor
DataFrame.join()
- join方法也可以实现部分主键合并功能,但是使用join方法时,两个主键名必须相同
DataFrame.join(self, other, on=None, how="left", lsuffix="", rsuffix="", sort=False)
重叠合并数据
数据分析和处理过程中偶尔会出现两份数据的内容几乎一致的情况,但是某些特征一张表上是完整的,另一个表是缺失的。这是处理将数据一一对比然后填充的方法以外,还可以进行重叠合并数据。
df.combine_first(other)
,other表示参与重叠合并的另一个表
2、清洗数据
数据重复会导致数据的方差变小,数据分布发生较大变化。缺失会导致样本信息减少,不仅增加了数据分析的困难,而且会导致数据分析结果产生偏差。异常值则会产生"伪回归"。
检测与处理重复值
-
记录重复,一个或多个特征的某几条记录的值完全相同
DataFrame.drop_duplicates(subset=None, keep="first", inplace=False)
,该方法仅对DataFrame、Series有效参数名称 说明 subset 接收string、sequence。表示去重的列,默认为全部列 keep 接收特定的string。表示重复时保留第几个数据。first、last、false inplace 接收boolean。表示是否在原表上进行操作。默认为False -
特征重复,存在一个或多个特征名称不同,但数据完全相同
-
相似度矩阵进行特征去重:该方法只能对数值型重复特征去重
利用特征间的相似度将两个相似度为1的特征去除一个。在pandas中,相似度计算方法为
corr
。使用该方法计算相似度时默认为pearson法,可以通过method参数进行调节,还支持spearman
和kendall
法。 -
通过
DataFrame.equals
进行特征去重#作出特征相等矩阵 df.loc[i,j] = df.loc[:,i].equals(df.loc[:,j]) #遍历找出重复的列 if df.iloc[k,l]: #删除重复列 df.drop(df.columns[l])
-
-
某一列是否有重复值
df_new_1 = df_new.groupby(['User_id'])['Merchant_id'].nunique()
#在同一个'User_id'下,'Merchant_id'有多少个
df_new_1 = df_new.groupby(['User_id']['Merchant_id'].value_counts()
df_new_1 = df_new.groupby(['User_id'])['Merchant_id'].unique()
#返回具体的unique值
检测与处理缺失值
在了解缺失值(也叫控制)如何处理之前,首先要知道的就是什么是缺失值?直观上理解,缺失值表示的是“缺失的数据”。
可以思考一个问题:是什么原因造成的缺失值呢?其实有很多原因,实际生活中可能由于有的数据不全所以导致数据缺失,也有可能由于误操作导致数据缺失,又或者人为地造成数据缺失。
在DataFrame中被当作是缺失值来处理的有:
- Python对象中的None
- np中的np.nan,显示标签是"NaN"
- "NaN"是特殊的浮点数
其实如果DataFrame中存在Python中的None对象,一旦转化为dtype类型数据(object类型除外),None对象都会转成np.nan。
df_01=pd.DataFrame([1,2,None]) #在None转化为NaN过程中,object对象转化为float64,因为"NaN"是特殊的浮点数
df_01
df_01[0].dtype
##
type(np.nan)
df_01=pd.DataFrame([1,2,np.nan])
df_01
df_01[0].dtype
ser_01=pd.Series(range(2),dtype=object) #在None转化为NaN过程中,object对象不会转化为float64
ser_01[0]=None
ser_01
- 检测缺失值:
df.isnull().sum()
识别缺失值方法、df.notnull().sum()
识别非缺失值方法
-
删除法
删除法简单易行,但是会引起数据结构的变动,样本量的减少
删除法分为删除观测记录和删除特征两种。属于通过减少样本量来换取信息完整度的一种方法,pandas提供了
dropna
方法,通过参数控制,既可以删除观测记录,也可以删除特征
df.dropna(axis=0, how="any", thresh=None, subset=None, inplace=False)
参数名称 | 说明 |
---|---|
axis | 接收0或1.表示轴向,0表示行,1表示列 |
how | 接收特定的string。表示删除形式。any表示只要有缺失值就删除;all表示全部为缺失值时才删除 |
subset | 接收array。表示要进行去重的列/行。默认为所有 |
inplace | 接收boolean。表示是否在原表上操作 |
-
参数how
user_info_01.dropna(axis=1,how="all") #how="all"只有当该列(或行)全都为缺失值时,才会将该列删除 # user_info_01.dropna(axis=1,how="any") #how="any"只有当该列(或行)有一个缺失值,就会将该列删除
-
参数thresh
thresh参数设置的是:你想至少留下多少非缺失值!
user_info_01.dropna(thresh=8) #thresh=8只有当该行(可以设置axis=1来处理列)有8缺失值以上,就会将该行删除
-
参数subset
subset设定一个子集,子集中的列作为剔除缺失值的参考列:
user_info_01.dropna(axis=0,how="any") user_info_01.dropna(axis=0,how="any",subset=["Height","Weight"]) user_info_01.dropna(axis=0,how="any",subset=["Birthplace","Weapon"]) ##如何删除user_info_01中缺失值超过45%的字段。 user_info_01.dropna(axis=1,thresh=user_info_01.shape[0]*(1-0.45))
-
替换法
替换法难度较低,但是会影响数据的标准差,导致信息量的变动
替换法指用一个特定的值替换缺失值。特征分为数值型和类别型,两者出现缺失值时的处理方法也不同。
- 缺失值为数值型时,常用均值、中位数、众数等描述其集中趋势的统计量来代替缺失值
- 缺失值为类别型时,选择使用众数来替换缺失值
df.fillna( value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs )
参数名称 说明 value 接收scalar、dict、Series、DataFrame。表示用来替换缺失值的值。 method 接收特定的string。backfill、fill表示使用下一个非缺失值来填补缺失值 axis 接收0或1。表示轴向,默认为1 inplace 接收boolean。是否在原表上进行操作 limit 接收int。表示填补缺失值个数的上限。超过则不进行填补 -
插值法
- 线性插值法:针对已知的值求出线性方程,通过求解线性方程得到缺失值。只在自变量和因变量为线性关系时拟合才较为出色
- 多项式插值法:利用已知的值拟合一个多项式,使用现有的数据猫族这个多项式,在利用这个多项式求解缺失值。常见的有拉格朗日插值和牛顿插值。
- 样条插值:以可变样条来做出一条经过一系列点的光滑曲线的插值方法。
- 重心坐标插值:
pandas提供了interpolate的插值方法,能够进行上述部分的插值操作,但是Scipy库的
interpolate
模块更加全面。import numpy as np from scipy import interpolate inter_1d = interpolate.interp1d(x,y,kind='linear') large = interpolate.lagrange(x,y) spline = interpolate.make_interp_spline(x, y, k=3, t=None, bc_type=None, axis=0,check_finite=True) BI = interpolate.BarycentricInterpolator(x,y)
-
df.replace()
None、np.nan、NaT(时间数据类型的缺失值) 这些都是缺失值。
但是这些在 Pandas 的眼中是缺失值,有时候在我们人类的眼中,某些异常值我们也会当做缺失值来处理。 例如,在我们的存储的用户信息中,假定我们限定用户都是青年,出现了年龄为 40 的,我们就可以认为这是一个异常值。
再比如,我们都知道性别分为男性(male)和女性(female),在记录用户性别的时候,对于未知的用户性别都记为了 “unknown”,很明显,我们也可以认为“unknown”是缺失值。
此外,有的时候会出现空白字符串,这些也可以认为是缺失值。
对于上面的这种情况,我们可以使用 replace 方法来替换缺失值。
user_info_01.iloc[::2,4]="unknown" user_info_01
如果我们想要将上面DataFrame中所有的"unknown"都转为pandas能识别的缺失值形式np.nan,可以使用df.replace()的方法:
user_info_01.replace("unknown",np.nan)
指定到"Birthplace"这一列来进行替换缺失值,也是可以的:
user_info_01["Birthplace"]=user_info_01["Birthplace"].replace("unknown",np.nan)
检测与处理异常值
异常值是指数据中个别的数值明显偏离其余的值,有时也称为离群点,检测异常值就是检验数据中是否有输入错误以及是否有含有不合理的数据。
-
3σ原则
该原则就是先假设一组检测数据只含有随机误差,对原始数据进行计算处理得到标准差,然后按一定的概率确定一个区间,认为误差超过这个区间就属于异常。这种处理方法仅适用于正态或者近似正态分布的样本数据。
-
箱线图分析
箱线图提供了识别异常值的一个标准,即异常值被定义为小于QL-1.5IQR或者大于QU+.5IQR的值。QL为下四分位数,QU为上四分位数,IQR为四分位数间距,是QU与QL之差。
3、标准化数据
不同特征之间往往具有不同的量纲,由此造成的数值间的差异可能很大,在涉及空间距离计算或者梯度下降等情况时,不对其进行处理会影响数据分析结果的准确性。
离差标准化数据
离差标准化是对原始数据的一种线性变换,结果是将原始数据的数值映射到[0,1]区间:
X
∗
=
X
−
m
i
n
m
a
x
−
m
i
n
X^{*}=\frac{X-min}{max-min}
X∗=max−minX−min
- 数据的整体分布情况并未发生变化
- 若数据集某个数值很大,则离差标准化的值就会接近于0,并且相互之间差别不大。若遇到超过目前属性的[min,max]取值范围的情况会引起系统出错,这时需要重新定义min、max。
标准差标准化数据
标准差标准化也叫零均值标准化或z分数标准化,经过改方法处理的数据均值为0,方程为1:
X
∗
=
X
−
X
‾
δ
X^{*}=\frac{X-\overline{X}}{\delta}
X∗=δX−X
小数定标标准化数据
通过移动数据的小数位数,将数据映射到区间[-1,1],移动的小数位数取决于数据的绝对值的最大值:
X
∗
=
X
1
0
k
X^{*}=\frac{X}{10^{k}}
X∗=10kX
4、转换数据
哑变量处理类别型数据
- 即0-1化非数值型数据
pandas库中的get_dummies
函数对类别型特征进行哑变量处理
pd.get_dummies(
data, #接收需要进行哑变量处理的数据
prefix=None, #哑变量化后列名的前缀
prefix_sep="_", #前缀连接符
dummy_na=False, #是否为NaN值添加一列
columns=None, #需要编码的列名
sparse=False, #虚拟列是否是稀疏的
drop_first=False,#是否通过从k个分类级别中删除第一级来获得k-1个分类级别
dtype=None,
)
离散化连续型数据
连续特征的离散化就是在数据的取值范围内设定若干个离散的划分点,将取值范围划分为一些离散化的区间,最后用不同的符号或者整数值代表落在每个子区间中的数据值。因此离散化涉及两个子任务:
- 确定分类数
- 如何将连续数据映射到这些类别型数据上
-
等宽法
将数据的值域分成具有相同宽度的区间,区间个数由数据本身的特点决定或者由用户指定。
pd.cut( x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False, duplicates="raise", )
参数名称 说明 x 接收需要离散化的数据 bins 接收int、list、array、tuple。int表示离散化后的数目;序列类型表示进行切分的区间,每两个数的间隔为一个区间 right 代表右侧是否为闭区间 labels 离散化后各个类别的名称 retbins 是否返回区间的标签 precision 显示标签的精度 pd.cut(user_infor['Age'], 7)
cut 自动生成了等距的离散区间,如果自己想定义也是没问题的。
pd.cut(user_infor.Age, [0, 18, 60, 100,5000])
有时候离散化之后,想要给每个区间起个名字,可以在pd.cut()中使用参数 labels 来指定。
a=pd.cut(user_infor.Age, [0, 18, 60, 100,150,5000], labels=["未成年", "成年", "老年人","超长寿","非人类"]) a #既然pd.cut()返回的是一列Series,那么可以将其添加到原DataFrame中: user_infor["Age"]=a user_infor
除了可以使用 cut 进行离散化之外,qcut 也可以实现离散化。cut 是根据每个值的大小来进行离散化的,qcut 是根据每个值出现的次数来进行离散化,也就是基于分位数的离散化功能。
pd.qcut()
user_infor=pd.read_csv("new_infor.csv",index_col="Unnamed: 0") user_infor pd.qcut(user_infor.Age, 3)
-
等频法
def SameRateCut(data,k): w = data.quantile(np.arange(0,1+1.0/k,1.0/k)) data = pd.cut(data,w) return data
-
聚类分析法
5、排序功能
在进行数据分析时,少不了进行数据排序。Pandas 支持两种排序方式:按轴(索引或列)排序和按实际值排序。
df.sort_index()
sort_index
方法默认是按照索引进行正序排的
user_info=pd.read_csv("new_infor.csv",index_col="Unnamed: 0")
user_infor.sort_index()
# 如果想要按照列进行倒序排,可以设置参数 axis=1 和 ascending=False。
user_infor.sort_index(axis=0, ascending=False)
df.sort_values()
如果想要实现按照实际值来排序,例如想要按照年龄排序,如何实现呢?
使用 sort_values 方法,设置参数 by=“age” 即可。
user_infor.sort_values(by='Age')
有时候我们可能需要按照多个值来排序,例如:按照年龄和城市来一起排序,可以设置参数 by 为一个 list 即可。
注意:list 中每个元素的顺序会影响排序优先级的。
user_infor.sort_values(by=["Age", "Birthplace"])
series.nlargest()
一般在排序后,我们可能需要获取最大的n个值或最小值的n个值,我们可以使用 nlargest 和 nsmallest 方法来完成,这比先排序再使用 head(n)方法快得多。
user_infor.Age.nlargest(3)
user_infor.Age.nsmallest(3)
6、函数应用及映射方法
虽说 Pandas 为我们提供了非常丰富的函数,有时候我们可能需要自己使用高级函数来实现自定义功能,并将它应用到 DataFrame 或 Series。常用到的函数有
- map
- apply
- applymap
Series.map()
map 是 Series 中特有的方法,通过它可以对 Series 中的每个元素实现转换。
如果我想通过年龄判断用户是否属于中年人(50岁以上为中年),通过 map 可以轻松搞定它。
##第一种方法
di = {
"纽约皇后区": "地球人",
"泰坦星球": "外星人",
"费城": "地球人",
"纽约": "地球人",
"哥谭":"地球人",
"阿斯加德":"外星人",
"天堂岛":"地球人",
"斯大林格勒":"地球人"
}
user_infor['星球'] = user_infor.Birthplace.map(di)
#第二种方法
earth_city=['纽约','费城','纽约','哥谭','天堂岛',"斯大林格勒"]
def city(x):
if x in earth_city:
return "地球人"
else:
return "外星人"
user_infor=user_info.copy(deep=True)
user_infor['种族'] = user_infor.Birthplace.map(city)
user_infor
Series.apply()
apply 方法既支持 Series,也支持 DataFrame,在对 Series 操作时会作用到每个值上,在对 DataFrame 操作时会作用到所有行或所有列(通过 axis参数控制)。
# 对 Series 来说,apply 方法 与 map 方法区别不大。
earth_city=['纽约','费城','纽约','哥谭','天堂岛',"斯大林格勒"]
def city(x):
if x in earth_city:
return "地球人"
else:
return "外星人"
user_infor['种族'] = user_infor.Birthplace.apply(city) #这里只是将上一小节中的Series.map()换成了.apply(),其余代码一样
user_infor
对 DataFrame 来说,apply 方法的作用对象是一行或一列数据(一个Series)
- axis为 0或’index’:将函数应用于每列。
- axis为1或’columns’:将函数应用于每一行。
def re_max(x):
return x.max()
user_infor.apply(re_max, axis=0)
df.applymap()
applymap 方法针对于 DataFrame,它作用于 DataFrame 中的每个元素,它对 DataFrame 的效果类似于 apply 对 Series 的效果。
#将'侠'替换成'人'
def replace(x):
x=str(x)
if x.find("侠")!=-1:
x=x.replace("侠","人")
return x
user_infor.applymap(replace)