1. Pandas读取数据
- 1pandas读取纯文本文件
- 读取csv文件
- 读取txt文件
(1)读取CSV,使用默认的标题行、逗号分隔符
fpath = "./datas/ml-latest-small/ratings.csv"
ratings = pd.read_csv(fpath)
# 查看每列的数据类型
ratings.dtypes
(2)读取txt文件,自己指定分隔符、列名
fpath = "./datas/crazyant/access_pvuv.txt"
pvuv = pd.read_csv(
fpath,
sep="\t",(制表符)
header=None,
names=['pdate', 'pv', 'uv']
)
1.2pandas读取xlsx格式excel文件
fpath = "./datas/crazyant/access_pvuv.xlsx"
pvuv = pd.read_excel(fpath)
1.3pandas读取mysql数据表
import pymysql
conn = pymysql.connect(
host='127.0.0.1',
user='root',
password='12345678',
database='test',
charset='utf8'
)
mysql_page = pd.read_sql("select * from crazyant_pvuv", con=conn)
其他:
# 查看数据的形状,返回(行数、列数)
ratings.shape
# 查看列名列表
ratings.columns
# 查看索引列
ratings.index
# 查看每列的数据类型
ratings.dtypes
2.Pandas数据结构
2.1. Series
Series是一种类似于一维数组的对象,它由一组数据(不同数据类型)以及一组与之相关的数据标签(即索引)组成。
(1)仅有数据列表即可产生最简单的Series
s1 = pd.Series([1,'a',5.2,7])
(2)创建一个具有标签索引的Series
s2 = pd.Series([1, 'a', 5.2, 7], index=['d','b','a','c'])
(3)使用Python字典创建Series
sdata={'Ohio':35000,'Texas':72000,'Oregon':16000,'Utah':5000}
s3=pd.Series(sdata)
(4) 根据标签索引查询数据
s2['a']
type(s2[['b','a']])同级【【】】
其他:
# 获取索引
s1.index
# 获取数据
s1.values
2.2 DataFrame
DataFrame是一个表格型的数据结构
- 每列可以是不同的值类型(数值、字符串、布尔值等)
- 既有行索引index,也有列索引columns
- 可以被看做由Series组成的字典
(1)创建dataframe最常用的方法,见02节读取纯文本文件、excel、mysql数据库
(2) 根据多个字典序列创建dataframe
data={
'state':['Ohio','Ohio','Ohio','Nevada','Nevada'],
'year':[2000,2001,2002,2001,2002],
'pop':[1.5,1.7,3.6,2.4,2.9]
}
df = pd.DataFrame(data)
其他:
df.columns
2.3 从DataFrame中查询出Series
- 如果只查询一行、一列,返回的是pd.Series
- 如果查询多行、多列,返回的是pd.DataFrame
查询一列,结果是一个pd.Series
df['year']
type(df['year'])
查询多列,结果是一个pd.DataFrame
df[['year', 'pop']]
type(df[['year', 'pop']])
查询一行,结果是一个pd.Series
df.loc[0]
查询多行,结果是一个pd.DataFrame
df.loc[0:3]
3. Pandas查询数据
- df.loc方法,根据行、列的标签值查询
- df.iloc方法,根据行、列的数字位置查询
- df.where方法
- df.query方法
.loc既能查询,又能覆盖写入,强烈推荐!
使用值列表批量查询
# 得到DataFrame
df.loc[['2018-01-03','2018-01-04','2018-01-05'], ['bWendu', 'yWendu']]
使用数值区间进行范围查询
# 行和列都按区间查询
df.loc['2018-01-03':'2018-01-05', 'bWendu':'fengxiang']
布尔条件---简单条件查询,最低温度低于-10度的列表
df.loc[df["yWendu"]<-10, :]
使用条件表达式查询
## 查询最高温度小于30度,并且最低温度大于15度,并且是晴天,并且天气为优的数据
df.loc[(df["bWendu"]<=30) & (df["yWendu"]>=15) & (df["tianqi"]=='晴') & (df["aqiLevel"]==1), :]
调用函数查询
# 直接写lambda表达式
df.loc[lambda df : (df["bWendu"]<=30) & (df["yWendu"]>=15), :]
# 编写自己的函数,查询9月份,空气质量好的数据,调用日期列转换成字符串index.str,
def query_my_data(df):
return df.index.str.startswith("2018-09") & (df["aqiLevel"]==1)
df.loc[query_my_data, :]
4. Pandas新增数据列
(1)直接赋值的方法
# 替换掉温度的后缀℃
df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃", "").astype('int32')
df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃", "").astype('int32')
# 注意,df["bWendu"]其实是一个Series,后面的减法返回的是Series
df.loc[:, "wencha"] = df["bWendu"] - df["yWendu"]
(2)df.apply方法
实例:添加一列温度类型:
- 如果最高温度大于33度就是高温
- 低于-10度是低温
- 否则是常温
def get_wendu_type(x):
if x["bWendu"] > 33:
return '高温'
if x["yWendu"] < -10:
return '低温'
return '常温'
# 注意需要设置axis==1,这是series的index是columns
df.loc[:, "wendu_type"] = df.apply(get_wendu_type, axis=1)
# 查看温度类型的计数
df["wendu_type"].value_counts()
(3)df.assign方法
# 可以同时添加多个新的列
df.assign(
yWendu_huashi = lambda x : x["yWendu"] * 9 / 5 + 32,
# 摄氏度转华氏度
bWendu_huashi = lambda x : x["bWendu"] * 9 / 5 + 32
)
(4)按条件选择分组分别赋值
按条件先选择数据,然后对这部分数据赋值新列
# 先创建空列(这是第一种创建新列的方法)
df['wencha_type'] = ''
df.loc[df["bWendu"]-df["yWendu"]>10, "wencha_type"] = "温差大"
df.loc[df["bWendu"]-df["yWendu"]<=10, "wencha_type"] = "温差正常"
5.Pandas数据统计函数
(1)汇总类统计
# 一下子提取所有数字列统计结果
df.describe()
## 查看单个Series的数据
df["bWendu"].mean()
# 最高温
df["bWendu"].max()
# 最低温
df["bWendu"].min()
(2)唯一去重和按值计数
一般不用于数值列,而是枚举、分类列
唯一性去重
df["fengxiang"].unique()
按值计数
df["fengxiang"].value_counts()
(3)相关系数和协方差
- 协方差:衡量同向反向程度,如果协方差为正,说明X,Y同向变化,协方差越大说明同向程度越高;如果协方差为负,说明X,Y反向运动,协方差越小说明反向程度越高。-~<---0--->+~
- 相关系数:衡量相似度程度,当他们的相关系数为1时,说明两个变量变化时的正向相似度最大,当相关系数为-1时,说明两个变量变化的反向相似度最大 -1<---0--->1
# 协方差矩阵:
df.cov()
# 相关系数矩阵
df.corr()
# 单独查看空气质量和最高温度的相关系数
df["aqi"].corr(df["bWendu"])
# 空气质量和温差的相关系数
df["aqi"].corr(df["bWendu"]-df["yWendu"])
6.Pandas对缺失值的处理
Pandas使用这些函数处理缺失值:
- isnull和notnull:检测是否是空值,可用于df和series
- dropna:丢弃、删除缺失值
- axis : 删除行还是列,{0 or ‘index’, 1 or ‘columns’}, default 0
- how : 如果等于any则任何值为空都删除,如果等于all则所有值都为空才删除
- inplace : 如果为True则修改当前df,否则返回新的df
- fillna:填充空值
- value:用于填充的值,可以是单个值,或者字典(key是列名,value是值)
- method : 等于ffill使用前一个不为空的值填充forword fill;等于bfill使用后一个不为空的值填充backword fill
- axis : 按行还是列填充,{0 or ‘index’, 1 or ‘columns’}
- inplace : 如果为True则修改当前df,否则返回新的df
(1)studf.isnull()
studf["分数"].notnull()
# 筛选没有空分数的所有行
studf.loc[studf["分数"].notnull(), :]
(2)#删除掉全是空值的列¶
studf.dropna(axis="columns", how='all', inplace=True)
#删除掉全是空值的行
studf.dropna(axis="index", how='all', inplace=True)
(3)将分数列为空的填充为0分
studf.fillna({"分数":0})
# 等同于
studf.loc[:, '分数'] = studf['分数'].fillna(0)
(4)使用前面的有效值填充,用ffill:forward fill
studf.loc[:, '姓名'] = studf['姓名'].fillna(method="ffill")
7.Pandas的SettingWithCopyWarning报警
# 只选出3月份的数据用于分析
condition = df["ymd"].str.startswith("2018-03")
发出警告的代码 df[condition]["wen_cha"] = df["bWendu"]-df["yWendu"]
相当于:df.get(condition).set(wen_cha),第一步骤的get发出了报警
链式操作其实是两个步骤,先get后set,get得到的dataframe可能是view也可能是copy,pandas发出警告
(1)解决方法1
将get+set的两步操作,改成set的一步操作
df.loc[condition, "wen_cha"] = df["bWendu"]-df["yWendu"]
(2)解决方法2
如果需要预筛选数据做后续的处理分析,使用copy复制dataframe
df_month3 = df[condition].copy()
df_month3["wen_cha"] = df["bWendu"]-df["yWendu"]
总之,pandas不允许先筛选子dataframe,再进行修改写入
要么使用.loc实现一个步骤直接修改源dataframe
要么先复制一个子dataframe再一个步骤执行修改
8.Pandas数据排序
Series的排序:
Series.sort_values(ascending=True, inplace=False)
参数说明:
- ascending:默认为True升序排序,为False降序排序
- inplace:是否修改原始Series
DataFrame的排序:
DataFrame.sort_values(by, ascending=True, inplace=False)
参数说明:
- by:字符串或者List<字符串>,单列排序或者多列排序
- ascending:bool或者List,升序还是降序,如果是list对应by的多列
- inplace:是否修改原始DataFrame
df["aqi"].sort_values(ascending=False)
单列排序
df.sort_values(by="aqi")
多列排序
# 按空气质量等级、最高温度排序,默认升序
df.sort_values(by=["aqiLevel", "bWendu"])
# 分别指定升序和降序
df.sort_values(by=["aqiLevel", "bWendu"], ascending=[True, False])
9.Pandas字符串处理
Pandas的字符串处理:
- 使用方法:先获取Series的str属性,然后在属性上调用函数;
- 只能在字符串列上使用,不能数字列上使用;
- Dataframe上没有str属性和处理方法
- Series.str并不是Python原生字符串,而是自己的一套方法,不过大部分和原生str很相似;
# 字符串替换函数
df["bWendu"].str.replace("℃", "")
# 判断是不是数字
df["bWendu"].str.isnumeric()
需要多次str处理的链式操作
# slice就是切片语法,可以直接用
df["ymd"].str.replace("-", "").str[0:6]
使用正则表达式的处理
# 方法2:正则表达式替换
df["中文日期"].str.replace("[年月日]", "")
10.Pandas的axis参数怎么理解
- axis=0或者"index":
- 如果是单行操作,就指的是某一行
- 如果是聚合操作,指的是跨行cross rows
- axis=1或者"columns":
- 如果是单列操作,就指的是某一列
- 如果是聚合操作,指的是跨列cross columns
按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动
# 代表的就是删除某列
df.drop("A", axis=1)
# 代表的就是删除某行
df.drop(1, axis=0)
跨行# axis=0 or axis=index
df.mean(axis=0)
跨列# axis=1 or axis=columns
df.mean(axis=1)
def get_sum_value(x):
return x["A"] + x["B"] + x["C"] + x["D"]
df["sum_value"] = df.apply(get_sum_value, axis=1)
11.Pandas的索引index的用途
index的用途总结:
- 更方便的数据查询;
- 使用index可以获得性能提升;
- 自动的数据对齐功能;
- 更多更强大的数据结构支持;
(1)使用index查询数据
# 使用index的查询方法
df.loc[500].head(5)
# 使用column的condition查询方法
df.loc[df["userId"] == 500].head()
(2)使用index会提升查询性能
- 如果index是唯一的,Pandas会使用哈希表优化,查询性能为O(1);
- 如果index不是唯一的,但是有序,Pandas会使用二分查找算法,查询性能为O(logN);
- 如果index是完全随机的,那么每次查询都要扫描全表,查询性能为O(N);
排序 df_sorted = df_shuffle.sort_index()
%timeit df_sorted.loc[500]
(3)使用index能自动对齐数据
(4)
很多强大的索引数据结构
- CategoricalIndex,基于分类数据的Index,提升性能;
- MultiIndex,多维索引,用于groupby多维聚合后结果等;
- DatetimeIndex,时间类型索引,强大的日期和时间的方法支持;
12.Pandas怎样实现DataFrame的Merge
Pandas的Merge,相当于Sql的Join,将不同的表按key关联到一个表
merge的语法:
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=True, suffixes=('_x', '_y'), copy=True, indicator=False, validate=None)
- left,right:要merge的dataframe或者有name的Series
- how:join类型,'left', 'right', 'outer', 'inner'
- on:join的key,left和right都需要有这个key
- left_on:left的df或者series的key
- right_on:right的df或者seires的key
- left_index,right_index:使用index而不是普通的column做join
- suffixes:两个元素的后缀,如果列有重名,自动添加后缀,默认是('_x', '_y')
df_ratings_users_movies = pd.merge(
df_ratings_users, df_movies, left_on="MovieID", right_on="MovieID", how="inner"
)
所有列名都有
如果出现非Key的字段重名怎么办
13.Pandas实现数据的合并concat
使用场景:
批量合并相同格式的Excel、给DataFrame添加行、给DataFrame添加列
一句话说明concat语法:
- 使用某种合并方式(inner/outer)
- 沿着某个轴向(axis=0/1)
- 把多个Pandas对象(DataFrame/Series)合并成一个。
concat语法:pandas.concat(objs, axis=0, join='outer', ignore_index=False)
- objs:一个列表,内容可以是DataFrame或者Series,可以混合
- axis:默认是0代表按行合并,如果等于1代表按列合并
- join:合并的时候索引的对齐方式,默认是outer join,也可以是inner join
- ignore_index:是否忽略掉原来的数据索引
append语法:DataFrame.append(other, ignore_index=False)
append只有按行合并,没有按列合并,相当于concat按行的简写形式
- other:单个dataframe、series、dict,或者列表
- ignore_index:是否忽略掉原来的数据索引
(1)使用pandas.concat合并数据
添加一列Series
s1 = pd.Series(list(range(4)), name="F")
pd.concat([df1,s1], axis=1)
添加多列Series***lambda x每一行的字典:x["A"]A这一列每一行的值+"_GG"
s2 = df1.apply(lambda x:x["A"]+"_GG", axis=1)
pd.concat([df1,s1,s2], axis=1)
(2)使用DataFrame.append按行合并数据
df1.append(df2, ignore_index=True)
for i in range(5):
# 注意这里每次都在复制
df = df.append({'A': i}, ignore_index=True)
df
# 第一个入参是一个列表,避免了多次复制
pd.concat(
[pd.DataFrame([i], columns=['A']) for i in range(5)],
ignore_index=True
)
14.Pandas批量拆分Excel与合并Excel
work_dir="./course_datas/c15_excel_split_merge"
#f是format的高级引入方法,格式化字符串
df_source = pd.read_excel(f"{work_dir}/crazyant_blog_articles_source.xlsx")
(1)将一个大Excel等份拆成多个Excel
# 每个人的任务数目,//向负无穷方向取整后
split_size = total_row_count // len(user_names)
if total_row_count % len(user_names) != 0:
split_size += 1
拆分成多个dataframe
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,for循环传进参数是数组
df_subs = []
for idx, user_name in enumerate(user_names):
# iloc的开始索引
begin = idx*split_size
# iloc的结束索引
end = begin+split_size
# 实现df按照iloc拆分
df_sub = df_source.iloc[begin:end]
# 将每个子df存入列表
df_subs.append((idx, user_name, df_sub))
遍历的是列表,将df存入的是列表,遍历列表,加文件名,将df存入命名好的excel
for idx, user_name, df_sub in df_subs:
file_name = f"{splits_dir}/crazyant_blog_articles_{idx}_{user_name}.xlsx"
df_sub.to_excel(file_name, index=False)
(2)合并多个小Excel到一个大Excel
遍历文件夹,得到要合并的Excel名称列表
分别读取到dataframe
使用pd.concat进行合并
df_merged = pd.concat(df_list)
15.Pandas怎样实现groupby分组统计
groupby:先对数据分组,然后在每个分组上应用聚合函数、转换函数
df.groupby(['A','B']).mean()
df.groupby(['A','B'], as_index=False).mean()
# 方法1:预过滤,性能更好
df.groupby('A')['C'].agg([np.sum, np.mean, np.std])
不同列使用不同的聚合函数
df.groupby('A').agg({"C":np.sum, "D":np.mean})
# 新增一列为月份
df['month'] = df['ymd'].str[:7]
#agg是使用多个聚合函数
group_data = df.groupby('month').agg({"bWendu":np.max, "yWendu":np.min, "aqi":np.mean})
16.Pandas的分层索引MultiIndex
(1)Series的分层索引MultiIndex
# unstack把二级索引变成列
ser.unstack()
#将索引变成列
ser.reset_index()
Series有多层索引MultiIndex怎样筛选数据?
#筛选第一个索引
ser.loc['BIDU']
# 多层索引,可以用元组的形式筛选
ser.loc[('BIDU', '2019-10-02')]
#筛选第二个索引
ser.loc[:, '2019-10-02']
(2)DataFrame的多层索引MultiIndex
- 元组(key1,key2)代表筛选多层索引,其中key1是索引第一级,key2是第二级,比如key1=JD, key2=2019-10-02
- 列表[key1,key2]代表同一层的多个KEY,其中key1和key2是并列的同级索引,比如key1=JD, key2=BIDU
#筛选多级索引用元组括起来
stocks.loc[('BIDU', '2019-10-02'), :]
#[]表示并列筛选多个值
stocks.loc[['BIDU', 'JD'], :]
# slice(None)代表筛选这一索引的所有内容
stocks.loc[(slice(None), ['2019-10-02', '2019-10-03']), :]
17.Pandas的数据转换函数map、apply、applymap
数据转换函数对比:map、apply、applymap:
- map:只用于Series,实现每个值->值的映射;
- apply:用于Series实现每个值的处理,用于Dataframe实现某个轴的Series的处理;
- applymap:只能用于DataFrame,用于处理该DataFrame的每个元素;
# 公司股票代码到中文的映射,注意这里是小写
dict_company_names = {
"bidu": "百度",
"baba": "阿里巴巴",
"iq": "爱奇艺",
"jd": "京东"
}
17.1. map用于Series值的转换
实例:将股票代码英文转换成中文名字
Series.map(dict) or Series.map(function)均可
方法1:Series.map(dict)
#str.lower()变成小写去map里找,直接实现value的映射
stocks["公司中文1"] = stocks["公司"].str.lower().map(dict_company_names)
方法2:Series.map(function)¶
function的参数是Series的每个元素的值
#lambda x 即series每个值
#lambda x : dict_company_names[x.lower()] value形成新的series,
stocks["公司中文2"] = stocks["公司"].map(lambda x : dict_company_names[x.lower()])
17.2. apply用于Series和DataFrame的转换
- Series.apply(function), 函数的参数是每个值
- DataFrame.apply(function), 函数的参数是Series
Series.apply(function)
function的参数是Series的每个值
stocks["公司中文3"] = stocks["公司"].apply(
lambda x : dict_company_names[x.lower()])
DataFrame.apply(function)
function的参数是对应轴的Series
stocks["公司中文4"] = stocks.apply(
lambda x : dict_company_names[x["公司"].lower()],
axis=1)
17.3. applymap用于DataFrame所有值的转换
# 将这些数字取整数,应用于所有元素
sub_df.applymap(lambda x : int(x))
# 直接修改原df的这几列
stocks.loc[:, ['收盘', '开盘', '高', '低', '交易量']] = sub_df.applymap(lambda x : int(x))