文章目录
1.Pandas数据读取
1.1 读取csv
pd.read_csv
查看数据的头尾
df.head(5)
df.tail(5)
查看数据的形状,返回(行数、列数)
df.shape
查看列名列表
df.columns
查看索引列
df.index
查看每列的数据类型
df.dtypes
1.2 读取txt
df = pd.read_csv(fpath, sep="\t", header=None, names=['a', 'b', 'c'])
1.3 读取excel文件
df = pd.read_excel(fpath)
1.4 读取MySQL数据库
import pymysql
conn = pymysql.connect(
host = '127.0.0.1',
user = 'root',
password = '123',
database = 'test',
charset = 'utf8'
)
df = pd.read_sql("select * from table", con=conn)
2.Pandas数据结构
2.1 Series
(1)仅有数据列表可产生最简单的Series
df = pd.Series([1, '1', 5.1, 6])
获取索引
df.index
获取数据
df.value
(2)创建一个具有标签索引的Series
df = pd.Series([1, '1', 5.1, 6], index=['a', 'b', 'c', 'd'])
(3)使用python字典创建Series
data = {'A': 1, 'B': 2, 'C': 3}
df = pd.Series(data)
(4)根据标签访问数据
df['A']
df[['A', 'B']]
2.2 DataFrame
(1)根据多个字典序列创建DataFrame
data = {
'A': [1, 2, 3, 4],
'B': [4, 5, 6, 7],
'C': [8, 9, 10, 11]
}
df = pd.DataFrame(data)
(2)从DataFrame中查询出Series
查询一列
df['A']
查询多列
df[['A', 'B']]
查询一行
df.loc[1]
查询多行
df.loc[1:3]
3.Pandas查询数据
3.1 读取数据
读取数据
df = pd.read_csv(fpath)
设置索引为日期
df.set_index('date', inplace=True)
替换温度的后缀℃——————5℃————>5
df.loc[:, 'temperature'] = df['temperature'].str.replace('℃', '').astype('int32')
3.2 使用单个Label值查询数据
得到单个值
df.loc['2020-05-01', 'temperature']
得到一个Series
df.loc['2020-05-01', ['temperature', 'tempe']]
3.3 使用值列表批量查询
得到Series
df.loc[['2020-05-01', '2020-05-02'], 'temperature']
得到DataFrame
df.loc[['2020-05-01', '2020-05-02'], ['temperature', 'tempe']]
3.4 使用数值区间进行范围查询
区间包含开始和结束
行index按区间
df.loc['2020-05-01': '2020-05-06', 'temperature']
列index按区间
df.loc['2020-05-01', 'temperature': 'A']
行和列都按区间
df.loc['2020-05-01': '2020-05-06', 'temperature': 'A']
3.5 使用条件表达式查询
查询温度低于10度的列表
df.loc['temperature'<10, :]
复杂条件查询
组合条件用&符号合并,每个条件判断都得带括号
查询最高温度小于30度,并且最低温度大于15度,并且是晴天,并且天气为优的数据
df.loc[(df['A']<=30) & (df['B']>=15) & (df['C']=='晴天') & (df['D']=='优'), :]
3.6 调用函数查询
直接写lambda表达式
df.loc[lambda df : (df['A']<=30) & (df['B']>=15), :]
编写自己的函数,查询9月份,空气质量好的数据
def query_data(df):
return df.index.str.startswith('2020-09') & (df['A']=='优')
df.loc[query_data, :]
4.Pandas新增数据列
4.1 直接赋值
计算温差,新增一列
df.loc[:, 'E'] = df['A'] - df['B']
4.2 apply方法
实例:添加一列温度类型
1.如果最高温度大于30度就是高温
2.如果低于-10度就是低温
3.否则是常温
def get_type(x):
if x['A'] > 30:
return 'High'
elif x['B'] < -10:
return 'Low'
else:
return 'Normal'
需要设置axis=1,这里Series的index是columns,如果axis=0就是index
df.loc[:, 'F'] = df.apply(get_type, axis=1)
查看温度类型的计数
df['F'].value_counts()
4.3 df.assign方法
实例:将温度从摄氏度变为华氏度
可以同时添加多个新的列
df.assign(
E = lambda x : x['A'] * 9 / 5 + 32,
F = lambda x : x['B'] * 9 / 5 + 32
)
4.4 按条件选择分组分别赋值
按条件先选择数据,然后对这部分数据赋值新列
实例:高低温差大于10度,则认为温差大
先创建空列
df['E'] = ''
df.loc[df['A']-df['B']>10, 'E'] = '温差大'
df.loc[df['A']-df['B']<=10, 'E'] = '温差正常'
5.Pandas数据统计函数
5.1 汇总类统计
提取所有数字列统计结果
df.describe()
查看单个Series的数据
df['A'].mean()
5.2 唯一去重和按值计数
唯一性去重
一般不用于数值列,而是枚举、分类列
df['A'].unique()
按值计数
df['A'].value_counts()
5.3 相关系数和协方差
协方差:衡量同向反向程度,如果协方差为正,说明X,Y同向变化,协方差越大说明同向程度越高;如果协方差为负,说明X,Y反向运动,协方差越小说明反向程度越高。
协方差矩阵
df.cov()
相关系数矩阵
df.corr()
df['A'].corr(df['B'])
6.Pandas数据缺失值处理
原始EXCEL文件
(1)读取EXCEL时,忽略前几个空行
df = pd.read_excel(fpath, skiprows=2)
(2)检测空值
df.isnull()
df.notnull()
筛选没有空分数的所有行
df.loc[df['分数'].notnull(), :]
(3)删除全是空值的列
df.dropna(axis='columns', how='all', inplace=True)
(4)删除全是空值的行
df.dropna(axis='index', how='all', inplace=True)
(5)将分数列为空的填充为0分
df.fillna({'分数': 0}, inplace=True)
等同于
df['分数'].fillna(0, inplace=True)
(6)将姓名的缺失值填充
使用前面的有效值填充,用ffill:forward fill
df.loc[:, '姓名'] = df['姓名'].fillna(method='ffill')
(7)将清洗好的EXCEL保存
df.to_excel(fpath2, index=False)
7.Pandas报警、原因、解决
8.Pandas对数据进行排序
8.1 Series排序
升序排序
df['A'].sort_values()
.
降序排序
ad['A'].sort_values(ascending=False)
8.2 DataFrame的排序
单列排序
df.sort_values(by='A')
df.sort_values(by='B', ascending=False)
多列排序
df.sort_values(by=['A', 'B'])
df.sort_values(by=['A', 'B'], ascending=False)
分别指定升序和降序
df.sort_values(by=['A', 'B'], ascending=[True, False])
9.Pandas字符串处理
9.1 获取Series的str属性,使用各种字符串处理函数
df['A'].str
字符串替换
df['A'].str.replace('C', '')
判断是不是数字
df['A'].str.isnumeric()
9.2 使用str的startswith、contains等得到bool的Series可以做条件查询
condition = df['A'].str.startswith('2020-5-1')
df[condition].head()
9.3 需要多次str处理的链式操作
怎样提取202005这样的数字月份?
1.先将日期2020-05-01替换成20200501的形式
2.提取月份字符串202005
df['A'].str.replace('-', '')
df['A'].str.replace('-', '').str.slice(0, 6)
df['A'].str.replace('-', '').str[0:6]
9.4 使用正则表达式处理
添加新列
def get_date(x):
year, month, day = x['A'].split('-')
return f"{year}年{month}月{day}日"
df['中文日期'] = df.apply(get_date, axis=1)
问题:怎样将“2020年5月1日”中的年、月、日三个中文字符去除?
方法一:链式replace
df['中文日期'].str.replace('年', '').str.replace('月', '').str.replace('日', '')
方法二:正则表达式替换
df['中文日期'].str.replace('[年月日]', '')
10.Pandas的axis参数
df = pd.DataFrame(
np.arange(12).reshape(3, 4),
columns=['A', 'B', 'C', 'D']
)
(1)单列drop,就是删除某一列
代表删除某列
df.drop('A', axis=1)
(2)单行drop,就是删除某一行
代表删除某行
df.drop(1, axis=0)
(3)按axis=0/index执行mean聚合操作
输出的不是每行的结果,而是每列的结果
df.mean(axis=0)
指定了哪个axis,就是这个axis要动起来,其他的axis保持不动
(4)按axis=1/icolumns执行mean聚合操作
输出的不是每行的结果,而是每列的结果
df.mean(axis=1)
(5)加深理解
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
11.1 使用index查询数据
drop=False,让索引列还保持在column
df.set_index('userId', inplace=True, drop=False)
使用index的查询方法
df.loc[500].head(5)
使用column的condition查询方法
df.loc[df['userId']==500].head()
11.2 使用index会提升查询性能
(1)完全随机的顺序查询
将数据随机打散
from sklearn.utils import shuffle
df_shuffle = shuffle(df)
df_shuffle.head()
索引是否是递增的
df_shuffle.index.is_monotonic_increasing
索引是否唯一
df_shuffle.index.is_unique
计时,查询id=500数据性能
%timeit df_shuffle.loc[500]
(2)将index排序后的查询
df_sorted = df_shuffle.sort_index()
%timeit df_shuffle.loc[500]
(3)index能自动对齐数据
s1 = pd.Series([1, 2, 3], index=list('abc'))
s2 = pd.Series([2, 3, 4], index=list('bcd'))
s1 + s2
(4)使用index可以有更多更强大的数据结构支持
12.Pandas实现DataFrame的Merge
12.1 电影数据集的join实例
(1)导入ratings表
import pandas as pd
df_ratings = pd.read_csv(
"./datas/movielens-1m/ratings.dat",
sep="::",
engine='python',
names="UserID::MovieID::Rating::Timestamp".split("::")
)
df_ratings.head()
(2)导入users表
df_users = pd.read_csv(
"./datas/movielens-1m/users.dat",
sep="::",
engine='python',
names="UserID::Gender::Age::Occupation::Zip-code".split("::")
)
df_users.head()
(3)导入movies表
df_movies = pd.read_csv(
"./datas/movielens-1m/movies.dat",
sep="::",
engine='python',
names="MovieID::Title::Genres".split("::")
)
df_movies.head()
(4)将ratings表和users表进行内连接
df_ratings_users = pd.merge(
df_ratings, df_users, left_on="UserID", right_on="UserID", how="inner"
)
df_ratings_users.head()
(5)将ratings_users表和movies表进行内连接
df_ratings_users_movies = pd.merge(
df_ratings_users, df_movies, left_on="MovieID", right_on="MovieID", how="inner"
)
df_ratings_users_movies.head(10)
12.2 理解merge时数量的对齐关系
(1)one-to-one 一对一关系的merge
left = pd.DataFrame({'sno': [11, 12, 13, 14],
'name': ['name_a', 'name_b', 'name_c', 'name_d']
})
right = pd.DataFrame({'sno': [11, 12, 13, 14],
'age': ['21', '22', '23', '24']
})
pd.merge(left, right, on='sno')
(2)one-to-many 一对多关系的merge
left = pd.DataFrame({'sno': [11, 12, 13, 14],
'name': ['name_a', 'name_b', 'name_c', 'name_d']
})
right = pd.DataFrame({'sno': [11, 11, 11, 12, 12, 13],
'grade': ['语文88', '数学90', '英语75','语文66', '数学55', '英语29']
})
数目以多的一边为准
pd.merge(left, right, on='sno')
(3)many-to-many 多对多关系的merge
left = pd.DataFrame({'sno': [11, 11, 12, 12,12],
'爱好': ['篮球', '羽毛球', '乒乓球', '篮球', "足球"]
})
right = pd.DataFrame({'sno': [11, 11, 11, 12, 12, 13],
'grade': ['语文88', '数学90', '英语75','语文66', '数学55', '英语29']
})
pd.merge(left, right, on='sno')
12.3 理解left join、right join、inner join、outer join的区别
left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key': ['K0', 'K1', 'K4', 'K5'],
'C': ['C0', 'C1', 'C4', 'C5'],
'D': ['D0', 'D1', 'D4', 'D5']})
(1)inner join,默认
左右两边的key都有,才会出现在结果里
pd.merge(left, right, how='inner')
(2)left join
左边的都会出现在结果里,右边的如果无法匹配则为Null
pd.merge(left, right, how='left')
(3)right join
右边的都会出现在结果里,左边的如果无法匹配则为Null
pd.merge(left, right, how='right')
(4)outer join
左边、右边的都会出现在结果里,如果无法匹配则为Null
pd.merge(left, right, how='outer')
12.4 重名字段
left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key': ['K0', 'K1', 'K4', 'K5'],
'A': ['A10', 'A11', 'A12', 'A13'],
'D': ['D0', 'D1', 'D4', 'D5']})
pd.merge(left, right, on='key')
pd.merge(left, right, on='key', suffixes=('_left', '_right'))
13.Pandas实现数据的合并concat
13.1 使用pandas.concat合并数据
(1)导入包,忽略警告
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
df1
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3'],
'E': ['E0', 'E1', 'E2', 'E3']
})
df2
df2 = pd.DataFrame({ 'A': ['A4', 'A5', 'A6', 'A7'],
'B': ['B4', 'B5', 'B6', 'B7'],
'C': ['C4', 'C5', 'C6', 'C7'],
'D': ['D4', 'D5', 'D6', 'D7'],
'F': ['F4', 'F5', 'F6', 'F7']
})
(2)默认的concat,参数为axis=0、join=outer、ignore_index=False
pd.concat([df1,df2])
(3)使用ignore_index=True可以忽略原来的索引
pd.concat([df1,df2], ignore_index=True)
(4)使用join=inner过滤不匹配的列
pd.concat([df1,df2], ignore_index=True, join="inner")
(5)使用axis=1相当于添加新列
添加一列Series
s1 = pd.Series(list(range(4)), name="F")
pd.concat([df1,s1], axis=1)
添加多列Series
s2 = df1.apply(lambda x:x["A"]+"_GG", axis=1)
s2.name="G"
pd.concat([df1,s1,s2], axis=1)
列表可以只有Series
pd.concat([s1,s2], axis=1)
列表是可以混合顺序的
pd.concat([s1,df1,s2], axis=1)
13.2 使用DataFrame.append按行合并数据
df1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
(1)给一个DataFrame添加另一个DataFrame
df1.append(df2)
(2)忽略原来的索引ingore_index=True
df1.append(df2, ignore_index=True)
(3)可以一行行的给DataFrame添加数据
一个空的df
df = pd.DataFrame(columns=['A'])
for i in range(5):
# 注意这里每次都在复制
df = df.append({'A': i}, ignore_index=True)
# 第一个入参是一个列表,避免了多次复制
pd.concat(
[pd.DataFrame([i], columns=['A']) for i in range(5)],
ignore_index=True
)
14.Pandas批量拆分与合并Excel
14.1 读取源Excel到Pandas
work_dir="./course_datas/c15_excel_split_merge"
splits_dir=f"{work_dir}/splits"
import os
if not os.path.exists(splits_dir):
os.mkdir(splits_dir)
import pandas as pd
df_source = pd.read_excel(f"{work_dir}/crazyant_blog_articles_source.xlsx")
df_source.head()
14.2 将一个大Excel等份拆成多个Excel
- 使用df.iloc方法,将一个大的DataFrame,拆分成多个小DataFrame
- 使用DataFrame.to_excel保存每个小Excel
(1)计算拆分后的每个Excel的行数
这个大excel,会拆分给这几个人
user_names = ["xiao_shuai", "xiao_wang", "xiao_ming", "xiao_lei", "xiao_bo", "xiao_hong"]
总行数
total_row_count = df_source.shape[0]
每个人的任务数目
split_size = total_row_count // len(user_names)
if total_row_count % len(user_names) != 0:
split_size += 1
(2)拆分成多个DataFrame
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))
(3)将每个DataFrame存入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)
14.3 合并多个小Excel到一个大Excel
- 遍历文件夹,得到要合并的Excel文件列表
- 分别读取到DataFrame,给每个df添加一列用于标记来源
- 使用pd.concat进行df批量合并
- 将合并后的DataFrame输出到Excel
(1)遍历文件夹,得到要合并的Excel名称列表
import os
excel_names = []
for excel_name in os.listdir(splits_dir):
excel_names.append(excel_name)
excel_names
(2)分别读取到DataFrame
df_list = []
for excel_name in excel_names:
读取每个excel到df
excel_path = f"{splits_dir}/{excel_name}"
df_split = pd.read_excel(excel_path)
得到username
username = excel_name.replace("crazyant_blog_articles_", "").replace(".xlsx", "")[2:]
print(excel_name, username)
给每个df添加1列,即用户名字
df_split["username"] = username
df_list.append(df_split)
(3)使用pd.concat进行合并
df_merged = pd.concat(df_list)
df_merged["username"].value_counts()
(4)将合并后的DataFrame输出到Excel
df_merged.to_excel(f"{work_dir}/crazyant_blog_articles_merged.xlsx", index=False)
15.Pandas的分组聚合groupby
类似SQL:
select city,max(temperature) from city_weather group by city;
groupby:先对数据分组,然后在每个分组上应用聚合函数、转换函数
import pandas as pd
import numpy as np
加上这一句,能在jupyter notebook展示matplot图表
%matplotlib inline
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'],
'B': ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],
'C': np.random.randn(8),
'D': np.random.randn(8)})
15.1 使用聚合函数做数据统计
(1)单个列groupby,查询所有数据列的统计
df.groupby('A').sum()
(2)多个列groupby,查询所有数据列的统计
df.groupby(['A','B']).mean()
df.groupby(['A','B'], as_index=False).mean()
(3)同时查看多种数据统计
df.groupby('A').agg([np.sum, np.mean, np.std])
(4)查看单列的结果数据统计
方法一:预过滤,性能更好
df.groupby('A')['C'].agg([np.sum, np.mean, np.std])
方法二:
df.groupby('A').agg([np.sum, np.mean, np.std])['C']
(5)不同列使用不同的聚合函数
df.groupby('A').agg({"C":np.sum, "D":np.mean})
15.2 遍历groupby的结果理解执行流程
for循环可以直接遍历每个group
(1)遍历单个列聚合的分组
g = df.groupby('A')
for name,group in g:
print(name)
print(group)
print()
可以获得单个分组的数据
g.get_group('bar')
(2)遍历多个列聚合的分组
g = df.groupby(['A', 'B'])
for name,group in g:
print(name)
print(group)
print()
g.get_group(('foo', 'one'))
可以直接查询group后的某几列,生成Series或者子DataFrame
g['C']
for name, group in g['C']:
print(name)
print(group)
print(type(group))
print()
15.3 实例分组探究天气数据
fpath = "./datas/beijing_tianqi/beijing_tianqi_2018.csv"
df = pd.read_csv(fpath)
# 替换掉温度的后缀℃
df.loc[:, "bWendu"] = df["bWendu"].str.replace("℃", "").astype('int32')
df.loc[:, "yWendu"] = df["yWendu"].str.replace("℃", "").astype('int32')
df.head()
新增一列为月份
df['month'] = df['ymd'].str[:7]
df.head()
(1)查看每个月的最高温度
data = df.groupby('month')['bWendu'].max()
data.plot()
(2)查看每个月的最高温度、最低温度、平均空气质量指数
group_data = df.groupby('month').agg({"bWendu":np.max, "yWendu":np.min, "aqi":np.mean})
group_data.plot()