import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets import load_boston
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # 替换sans-serif字体
plt.rcParams['axes.unicode_minus'] = False # 解决坐标轴负数的负号显示问题
#刻度的大小
plt.rcParams['axes.labelsize'] = 16
#线的粗细
plt.rcParams['lines.linewidth'] = 2
#x轴的大小
plt.rcParams['xtick.labelsize'] = 14
#y轴的大小
plt.rcParams['ytick.labelsize'] = 14
#图例大小
plt.rcParams['legend.fontsize'] = 14
#图的大小
plt.rcParams['figure.figsize'] = [12,8]
pip install -i https://pypi.douban.com/simple xlrd
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple xlrd
#read_table函数
#功能:从文件、URL、文件型对象中加载带分隔符的数据,默认分隔符为制表符("\t")。
#data.txt的内容还是不变,我们可以通过指定read_table的sep参数来修改默认的分隔符。
data = pd.read_table("data.txt",sep=",")
# read_excel函数
data1=pd.read_excel(r'C:\Users\admin\Desktop\satisfaction.xlsx')
data1=pd.read_excel(r'C:\Users\admin\Desktop\satisfaction.xlsx',sheet_name='Sheet1')
# 列表:指定多个需要读取的Sheet,列表的元素可以使索引,也可以是字符串,
# 例如[0, 1, 'Sheet3']表示读取第一张、第二张和名为Sheet3的3张Sheet,返回的数据是以列表元素为键包含数据的DataFrame对象为值的字典。
data = pd.read_excel('data.xlsx', sheet_name=[0, 1, 'Sheet3'])
# pip install -i https://pypi.douban.com/simple pymysql
import pandas as pd
import pymysql
conn = pymysql.connect(host="localhost",user='chb', password='123456', db="test",charset="utf8")
sql = 'select * from student'
df = pd.read_sql(sql, conn)
print(df)
conn.close()
# read_csv函数
#功能:从文件、URL、文件新对象中加载带有分隔符的数据,默认分隔符是逗号。
#encoding :编码,字符型,通常为'utf-8',如果中文读取不正常,可以将encoding设为’gbk‘。
# 读取csv文件
#默认将第一行作为标题
data = pd.read_csv("data.txt",encoding='gbk')
#设置header参数,读取文件的时候没有标题
data1 = pd.read_csv("data.txt",header=None)
#设置names参数,来设置文件的标题
data2 = pd.read_csv("data.txt",names=["a","b","c","d","name"])
#设置names参数,来设置文件的标题,设置index_col参数来设置列索引
data2 = pd.read_csv("data.txt",names=["a","b","c","d","name"],index_col="name")
df = pd.read_csv(
"./datas/movielens-1m/ratings.dat",
sep="::",
engine='python',
names="UserID::MovieID::Rating::Timestamp".split("::")
)
df.head(1)
python数据分析之pandas数据选取:df[] df.loc[] df.iloc[] df.ix[] df.at[] df.iat[]
在Dataframe中选取数据大抵包括3中情况:
1)行(列)选取(单维度选取):df[]。这种情况一次只能选取行或者列,即一次选取中,只能为行或者列设置筛选条件(只能为一个维度设置筛选条件)。
2)区域选取(多维选取):df.loc[],df.iloc[],df.ix[]。这种方式可以同时为多个维度设置筛选条件。
3)单元格选取(点选取):df.at[],df.iat[]。准确定位一个单元格。
data = {
'name': ['Joe', 'Mike', 'Jack', 'Rose', 'David', 'Marry', 'Wansi', 'Sidy', 'Jason', 'Even'],
'age': [25, 32, 18, np.nan, 15, 20, 41, np.nan, 37, 32],
'gender': [1, 0, 1, 1, 0, 1, 0, 0, 1, 0],
'isMarried': ['yes', 'yes', 'no', 'yes', 'no', 'no', 'no', 'yes', 'no', 'no']}
labels = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
df = pd.DataFrame(data, index=labels)
选取行\
选取行的方式包括三种:整数索引切片、标签索引切片和布尔数组。
df[0:2]
name | age | gender | isMarried | |
---|---|---|---|---|
a | Joe | 25.0 | 1 | yes |
b | Mike | 32.0 | 0 | yes |
df[df['age']>30]
name | age | gender | isMarried | |
---|---|---|---|---|
b | Mike | 32.0 | 0 | yes |
g | Wansi | 41.0 | 0 | no |
i | Jason | 37.0 | 1 | no |
j | Even | 32.0 | 0 | no |
df[(df['age']>30) & (df['isMarried']=='no')]
name | age | gender | isMarried | |
---|---|---|---|---|
g | Wansi | 41.0 | 0 | no |
i | Jason | 37.0 | 1 | no |
j | Even | 32.0 | 0 | no |
df[(df['age']==20) |(df['age']==32)]
name | age | gender | isMarried | |
---|---|---|---|---|
b | Mike | 32.0 | 0 | yes |
f | Marry | 20.0 | 1 | no |
j | Even | 32.0 | 0 | no |
注意:像上面这种通过多个布尔条件判断的情况,多个条件最好(一定)用括号括起来,否则非常容易出错。
列选取\
列选取方式也有三种:标签索引、标签列表、Callable对象
df[['name','age']]
name | age | |
---|---|---|
a | Joe | 25.0 |
b | Mike | 32.0 |
c | Jack | 18.0 |
d | Rose | NaN |
e | David | 15.0 |
f | Marry | 20.0 |
g | Wansi | 41.0 |
h | Sidy | NaN |
i | Jason | 37.0 |
j | Even | 32.0 |
df[lambda df:df.columns[0]]
a Joe
b Mike
c Jack
d Rose
e David
f Marry
g Wansi
h Sidy
i Jason
j Even
Name: name, dtype: object
df.loc[df['age']>30,['name','age']]
name | age | |
---|---|---|
b | Mike | 32.0 |
g | Wansi | 41.0 |
i | Jason | 37.0 |
j | Even | 32.0 |
df.loc[(df['name']=='Mike') |(df['name']=='Marry'),['name','age']]
name | age | |
---|---|---|
b | Mike | 32.0 |
f | Marry | 20.0 |
#选取第2行的第1列、第3列、第4列
df.iloc[1, [0,2,3]]
name Mike
gender 0
isMarried yes
Name: b, dtype: object
#直接写lambda表达式
df.loc[lambda df : (df['age']<=30) & (df['gender']==1), :].head(1)
name | age | gender | isMarried | |
---|---|---|---|---|
a | Joe | 25.0 | 1 | yes |
# 编写自己的函数
#def 函数名(参数列表):
# 函数体
Pandas中DateFrame修改列名
将a表A、B、C列名更改为a,b,c
1、暴力方法 但是缺点是必须写三个,要不报错
a.columns = [‘a’,‘b’,‘c’]
2、a.rename(columns={‘A’:‘a’, ‘B’:‘b’, ‘C’:‘c’}, inplace = True),可随意更改个数
如:a.rename(columns={‘A’:‘a’, ‘C’:‘c’}, inplace = True)\
data.rename(columns={
'Class': '舱位',
'Gender': '性别',
'Customer Type': '客户类型',
'Type of Travel': '出行类型',
'Age': '年龄',
'Flight Distance': '飞行距离',
'Seat comfort': '座椅舒适度',
'Departure/Arrival time convenient': 'OD时间便捷程度',
'Food and drink': '餐饮',
'Gate location': '登机门位置',
'Inflight wifi service': '机上WiFi服务',
'Inflight entertainment': '机上娱乐',
'On-board service': '机上服务',
'Ease of Online booking': '轻松在线预订',
'Online support': '网络支持',
'Leg room service': '腿部客房服务',
'Baggage handling': '行李搬运',
'Checkin service': '登机服务',
'Cleanliness': '清洁度',
'Online boarding': '在线登机',
'Departure Delay in Minutes': '出发延迟分钟',
'Arrival Delay in Minutes': '到达延迟分钟',
'satisfaction_v2': '满意度'
},
inplace=True)
1、使用replace实现:y_label = y_label[‘gender’].replace([‘male’, ‘female’], [1.0, 0.0])
2、使用map实现:
gender = {‘male’: 1.0, ‘female’: 0.0}
x_data[‘gender’] = x_data[‘gender’].map(gender)\
1、pandas 实现
将数据读入到 pandas 中,使用 get_dummies 方法即可。不过有时候转换后会出现数值错误,保险起见可将元素类型转换为 float
x_data = pd.get_dummies(x_data).astype(‘float’)
2、sklearn 实现
将数据读入到 pandas 中,使用 sklearn.preprocessing 中的 OneHotEncoder() 即可.
from sklearn import preprocessing as pp\
ohe = pp.OneHotEncoder()
gender = x_data[‘video_type’].values.reshape((-1, 1))
gender = ohe.fit_transform(gender).toarray()\
方式1:使用df[(df[‘a’]>3)&(df[‘b’]<5)]
方式2:使用 df.query(“a”>3 & “b”<5)
性能对比:
当数据量小时,使用方式1更快
当数据量大时,因为方法2直接使用C语言实现,节省方法1的临时多组的多次复制,方法2更快
data[(data[‘送达时长’]0)|(data[‘顾客配送评分’]1)]
等价于data.query('送达时长0’ or '顾客配送评分1’)
# 注意,df["bWendu"]其实是一个Series,后面的减法返回的是Series
df.loc[:, "wencha"] = df["bWendu"] - df["yWendu"]
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.assign(
yWendu_huashi = lambda x : x["yWendu"] * 9 / 5 + 32,
# 摄氏度转华氏度
bWendu_huashi = lambda x : x["bWendu"] * 9 / 5 + 32).head(1)
# 先创建空列(这是第一种创建新列的方法)
df['wencha_type'] = ''
df.loc[df["bWendu"]-df["yWendu"]>10, "wencha_type"] = "温差大"
df.loc[df["bWendu"]-df["yWendu"]<=10, "wencha_type"] = "温差正常"
df["wencha_type"].value_counts()
#设置切分区域
listBins = [0, 10, 20, 30, 40, 50, 60, 1000000]
#设置切分后对应标签
listLabels = ['0_10','11_20','21_30','31_40','41_50','51_60','61及以上']
#利用pd.cut进行数据离散化切分
"""
pandas.cut(x,bins,right=True,labels=None,retbins=False,precision=3,include_lowest=False)
x:需要切分的数据
bins:切分区域
right : 是否包含右端点默认True,包含
labels:对应标签,用标记来代替返回的bins,若不在该序列中,则返回NaN
retbins:是否返回间距bins
precision:精度
include_lowest:是否包含左端点,默认False,不包含
"""
df['fenzu'] = pd.cut(df['data'], bins=listBins, labels=listLabels, include_lowest=True)
rsplit和split()的用法类似,一个从右边开始,一个从左边开始。
如果直接用某一列和split()来分列是不行的,因为Series数据类型是没有split()的,所以需要先用.str将这一列转换为类似字符串的格式,就能够使用split()了。\
str.split()有三个参数:第一个参数就是引号里的内容:就是分列的依据,可以是空格,符号,字符串等等。
第二个参数就是前面用到的expand=True,这个参数直接将分列后的结果转换成DataFrame。\
第三个参数的n=数字就是限制分列的次数。就是当用于分列的依据符号在有多个的话需要指定分列的次数(不指定的话就会根据符号有几个分列几次)。
data1=pd.merge(data,(data['顾客配送评价标签'].str.split('|',expand=True,n=3)),how='left',left_index=True,right_index=True)
data['顾客配送评价标签1']=data['顾客配送评价标签'].map(lambda x:x.split('|')[0])
data['顾客配送评价标签2']=data['顾客配送评价标签'].map(lambda x:x.split('|')[1])
data['顾客配送评价标签3']=data['顾客配送评价标签'].map(lambda x:x.split('|')[2])
#该解法要求列里的分隔符数量一致
# 一下子提取所有数字列统计结果
df.describe()
# 对非数字列数据进行描述统计(并添加每个数据列缺失的比例)
df.select_dtypes(include=['O']).describe().T\
.assign(missing_pct=df.apply(lambda x:(len(x)-x.count())/len(x)))
# 唯一值去重 (一般不用于数值类型,而是枚举、分类型)
df["fengxiang"].unique()
pd.value_counts(
values,
sort=True, #是否排序,默认是要排序
ascending=False, #默认降序排列
normalize=False, #标准化、转化成百分比形式
bins=None, #可以自定义分组区间,默认是没有,但也可以自定义区间
dropna=True, #是否删除nan,默认删除
)
常规用法:
pd.value_counts()
df.value_counts()
df['字段'].value_counts()
pandas 计数函数value_counts()
# 唯一值去重后得到的个数
df["fengxiang"].nunique()
# 去除重复值
#DataFrame.drop_duplicates(subset=None, keep='first', inplace=False)
df.drop_duplicates(subset=['tianqi','fengxiang'],keep='first',inplace=False).head(1)
drop_duplicates参数说明:
参数subset
subset用来指定特定的列,默认所有列
参数keep
keep可以为first和last,表示是选择最前一项还是最后一项保留,默认first
参数inplace
inplace是直接在原来数据上修改还是保留一个副本,默认为False
示例:
df.drop_duplicates(subset = [‘ID’,‘Name’],keep=‘last’,inplace=True)\
数据源质量比较高,缺失值不到1%,直接删除缺失值即可。
1.删除所有缺失值所在行:data=data.dropna()
2.指定删除某列的缺失值所在行:data.dropna(subset=[‘age’]
3.删除缺失值所在的列:data=data.dropna(axis=1)
如果缺失的比较多,可以使用均值或众数等填充:data[‘age’]=data[‘age’].fillna(data[‘age’].mean())
也可使用插值函数来填写数字的缺失值。比如取数据框中缺失值上下的数字平均值,data[‘age’]=data[‘age’].fillna(data[‘age’].interpolate())
或者data.fillna(axis=1,method=‘ffill’)来横向(axis=1)/纵向(axis=0)用缺失值前面的值替换缺失值\
处理非标准缺失值,比如age列本应该是数字,但是却出现两个并不是数字也不是nan的异常值
可以通过使用replace函数先将其转换为NaN来处理此问题,然后根据需要,使用上面的方法处理缺失值。
data=data.replace([‘N岁’,’-’],np.nan)
对缺失值的处理
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
# 缺失值处理
df.isnull().sum()
# 或者data.info()
name 0
age 2
gender 0
isMarried 0
dtype: int64
1、np.isnan() 只有数组数值运算时可使用
注意:numpy模块的isnan方法仅支持对数值进行判断,因此传入的如果时字符串类型会报错。
如:筛选出客户满意度不为空的数据
df = data[np.isnan(data[‘qingjie’])==False] 可以正常运行,因为清洁列是数值型。
np.isnan(data[‘f_alarm_name’]) 就会报错,因为预警名称是字符串类型。
对于字符串类型的数据可以使用df = data[pd.isnull(data[‘f_alarm_name’])==False] 筛选
isnull () 主要判断字符型是不是有值,严格来说 isnull () 可以判断所有的空值,
但是python的数值字段比如int float 为空的时候默认是Nan,取不为空的数据:df[df.qingjie.notnull()]。
2、2、pd.iana()和pd.isnull() 功能一摸一样。
简单说呢就是:
numpy里边查找NaN值的话,就用np.isnan()。
pandas里边查找NaN值的话,要么.isna(),要么.isnull()
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.sort_values(by=["aqiLevel", "bWendu"], ascending=[True, False]).head(1)
https://blog.csdn.net/qq_40678222/article/details/83032178
\
使用方法:先获取Series的str属性,然后在属性上调用函数;
只能在字符串列上使用,不能数字列上使用;
Dataframe上没有str属性和处理方法
Series.str并不是Python原生字符串,而是自己的一套方法,不过大部分和原生str很相似;
# 字符串替换函数
df["bWendu"].str.replace("℃", "").head(1)
# 判断是不是数字
df["bWendu"].str.isnumeric().head(1)
#使用str的startswith、contains等得到bool的Series可以做条件查询
df[df['ymd'].str.startswith('2018-03')].head(1)
#需要多次str处理的链式操作
#怎样提取201803这样的数字月份?
#df["ymd"].str.replace("-", "").str.slice(0, 6)
# slice就是切片语法,可以直接用
df["ymd"].str.replace("-", "").str[0:6]
(1) format方法: 相对基本格式化输出采用‘%’的方法,format()功能更强大, 该函数把字符串当成一个模板,通过传入的参数进行格式化,并且使用大括号‘{}’作为特殊字符代替‘%’\
位置参数:"{} love {} “.format(‘wo’,‘ni’) :'wo love ni ’
关键字参数:”{a} love {b} “.format(b=‘wo’,a=‘ni’) : ‘ni love wo ’
两者混用时,未知参数一定写在关键字参数之前:
特殊:’{:.1f}{}’.format(20.56,‘GB’) :‘20.6GB’ (“:”冒号是格式化符号的开始,”.1"表示保留一位小数,"f"定点数) (2)%方法:字符串格式化符号含义\ https://www.cnblogs.com/qinchao0317/p/10699717.html
axis=0或者"index":
如果是单行操作,就指的是某一行
如果是聚合操作,指的是跨行cross rows
axis=1或者"columns":
如果是单列操作,就指的是某一列
如果是聚合操作,指的是跨列cross columns
按哪个axis,就是这个axis要动起来(类似被for遍历),其它的axis保持不动
index的用途总结:\
更方便的数据查询;
使用index可以获得性能提升;
自动的数据对齐功能;
更多更强大的数据结构支持;
如果index是唯一的,Pandas会使用哈希表优化,查询性能为O(1);
如果index不是唯一的,但是有序,Pandas会使用二分查找算法,查询性能为O(logN);
如果index是完全随机的,那么每次查询都要扫描全表,查询性能为O(N);
https://blog.csdn.net/weixin_42782150/article/details/89546357
Pandas的Merge,相当于Sql的Join,将不同的表按key关联到一个表\
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:左侧DataFrame中用于连接键的列名,这个参数在左右列名不同但代表的含义相同时非常有用
right_on:right的df或者seires的key
left_index,right_index:使用左/右侧DataFrame中的行索引作为连接键( 但是这种情况下最好用JOIN)。除了指定字段作为主键以外,还可以考虑用索引作为拼接的主键,leftindex和rightindex默认为False,就是不以索引作为主键,调整为True就可以了
suffixes:两个元素的后缀,如果列有重名,自动添加后缀,默认是(’_x’, ‘_y’)
copy:默认为True,总是将数据复制到数据结构中,设置为False可以提高性能
理解merge时数量的对齐关系 以下关系要正确理解:
one-to-one:一对一关系,关联的key都是唯一的
比如(学号,姓名) merge (学号,年龄)
结果条数为:11
one-to-many:一对多关系,左边唯一key,右边不唯一key
比如(学号,姓名) merge (学号,[语文成绩、数学成绩、英语成绩])
结果条数为:1N
many-to-many:多对多关系,左边右边都不是唯一的
比如(学号,[语文成绩、数学成绩、英语成绩]) merge (学号,[篮球、足球、乒乓球])
结果条数为:M*N
# a. 两张表df1和df2的列名有重叠,且重叠列的内容完全相同,直接用pd.merge(df1, df2)
#样集1
df1=pd.DataFrame(np.arange(12).reshape(3,4),columns=['a','b','c','d'])
#样集2
df2=pd.DataFrame({
'b':[1,5],'d':[3,7],'a':[0,4]})
#两张表df1和df2的列名有重叠,且重叠列的内容完全相同,直接用pd.merge(df1, df2)
pd.merge(df1,df2)
a | b | c | d | |
---|---|---|---|---|
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
#b. 两张表df1和df2的列名有重叠,但重叠列的内容完全不同,须使用pd.merge(df1, df2, left_index=True, right_index=True, how='left')
#(备注: 如果直接用pd.merge(df1, df2),将会得到一张空表,故必须指定行索引参数left_index, right_index,)
#样集1
df1=pd.DataFrame(np.arange(12).reshape(3,4),columns=['a','b','c','d'])
#样集2
df2=pd.DataFrame({
'b':[15,6],'d':[1,11],'a':[0,6]})
#b. 两张表df1和df2的列名有重叠,但重叠列的内容完全不同
pd.merge(df1, df2, left_index=True, right_index=True, how='left')
a_x | b_x | c | d_x | b_y | d_y | a_y | |
---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 15.0 | 1.0 | 0.0 |
1 | 4 | 5 | 6 | 7 | 6.0 | 11.0 | 6.0 |
2 | 8 | 9 | 10 | 11 | NaN | NaN | NaN |
使用场景: 批量合并相同格式的Excel、给DataFrame添加行、给DataFrame添加列\
一句话说明concat语法:
使用某种合并方式(inner/outer) 沿着某个轴向(axis=0/1) 把多个Pandas对象(DataFrame/Series)合并成一个。\
语法格式 pandas.concat(objs, axis=0, join=‘outer’, join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, sort=None, copy=True)\
参数说明\
objs:series,dataframe或者是panel对象构成的序列lsit
axis:指明连接的轴向, {0/’index’(行), 1/’columns’(列)},默认为0
join:指明连接方式 , {‘inner’(交集), ‘outer(并集)’},默认为outer
join_axes:自定义的索引。指明用其他n-1条轴的索引进行拼接, 而非默认join =’ inner’或’outer’方式拼接
keys:创建层次化索引。可以是任意值的列表或数组、元组数组、数组列表(如果将levels设置成多级数组的话)
ignore_index=True:重建索引
append只有按行合并,没有按列合并,相当于concat按行的简写形式\
other:单个dataframe、series、dict,或者列表
ignore_index:是否忽略掉原来的数据索引
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 = 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']
})
# 使用ignore_index=True可以忽略原来的索引
pd.concat([df1,df2], ignore_index=True)
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
0 | A0 | B0 | C0 | D0 | E0 | NaN |
1 | A1 | B1 | C1 | D1 | E1 | NaN |
2 | A2 | B2 | C2 | D2 | E2 | NaN |
3 | A3 | B3 | C3 | D3 | E3 | NaN |
4 | A4 | B4 | C4 | D4 | NaN | F4 |
5 | A5 | B5 | C5 | D5 | NaN | F5 |
6 | A6 | B6 | C6 | D6 | NaN | F6 |
7 | A7 | B7 | C7 | D7 | NaN | F7 |
# 使用join=inner过滤掉不匹配的列
pd.concat([df1,df2], ignore_index=True, join="inner")
A | B | C | D | |
---|---|---|---|---|
0 | A0 | B0 | C0 | D0 |
1 | A1 | B1 | C1 | D1 |
2 | A2 | B2 | C2 | D2 |
3 | A3 | B3 | C3 | D3 |
4 | A4 | B4 | C4 | D4 |
5 | A5 | B5 | C5 | D5 |
6 | A6 | B6 | C6 | D6 |
7 | A7 | B7 | C7 | D7 |
# 使用axis=1相当于添加新列
s1 = pd.Series(list(range(4)), name="F")
pd.concat([df1,s1], axis=1)
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
0 | A0 | B0 | C0 | D0 | E0 | 0 |
1 | A1 | B1 | C1 | D1 | E1 | 1 |
2 | A2 | B2 | C2 | D2 | E2 | 2 |
3 | A3 | B3 | C3 | D3 | E3 | 3 |
# 使用DataFrame.append按行合并数据
df1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
#忽略原来的索引ignore_index=True
df1.append(df2, ignore_index=True)
A | B | |
---|---|---|
0 | 1 | 2 |
1 | 3 | 4 |
2 | 5 | 6 |
3 | 7 | 8 |
1.基本语法
(1):df.groupby(‘以什么分组’)[‘以哪一些聚合’].sum( )
注意:如果是经过筛选的后的df,那么聚合列也应该是筛选后的
(2)于是否选择分组列作为索引
df.groupby([‘A’,‘B’]).mean()-----A、B是双层索引
df.groupby([‘A’,‘B’]).mean().reset_index() -----A、B不是双层索引
df.groupby([‘A’,‘B’], as_index=False).mean()-----------A、B不是双层索引
(3)同时查看多种数据统计
df.groupby(‘A’).agg([np.sum, np.mean, np.std])
(4) 查看单列的结果数据统计
方法1:预过滤,性能更好
df.groupby(‘A’)[‘C’].agg([np.sum, np.mean, np.std])
(5)不同列使用不同的聚合函数
df.groupby(‘A’).agg({“C”:np.sum, “D”:np.mean})
2.高级使用方法
电影评分数据集(UserID,MovieID,Rating,Timestamp)\
聚合后单列-单指标统计:每个MovieID的平均评分
df.groupby(“MovieID”)[“Rating”].mean()\
聚合后单列-多指标统计:每个MoiveID的最高评分、最低评分、平均评分
df.groupby(“MovieID”)[“Rating”].agg(mean=“mean”, max=“max”, min=np.min)
df.groupby(“MovieID”).agg({“Rating”:[‘mean’, ‘max’, np.min]})\
聚合后多列-多指标统计:每个MoiveID的评分人数,最高评分、最低评分、平均评分
df.groupby(“MovieID”).agg( rating_mean=(“Rating”, “mean”), user_count=(“UserID”, lambda x : x.nunique())
df.groupby(“MovieID”).agg( {“Rating”: [‘mean’, ‘min’, ‘max’], “UserID”: lambda x :x.nunique()})
df.groupby(“MovieID”).apply( lambda x: pd.Series( {“min”: x[“Rating”].min(), “mean”: x[“Rating”].mean()}))\
记忆:agg(新列名=函数)、agg(新列名=(原列名,函数))、agg({“原列名”:函数/列表})
agg函数的两种形式,等号代表“把结果赋值给新列”,字典/元组代表“对这个列运用这些函数”
#聚合后单列-单指标
df.groupby('MovieID')['Rating'].mean().head(1)
# 聚合后单列-多指标统计
# 方法1:agg函数传入多个结果列名=函数名形式
df.groupby('MovieID')['Rating'].agg(mean='mean',max='max',min=np.min).head(1)
#方法2:agg函数传入字典,key是column名,value是函数列表
df.groupby('MovieID').agg({
'Rating':['mean','max','min']})\
.rename(columns={
'mean':'age_mean','max':'age_max','min':'age_min'}).head(1)
#聚合后多列多指标统计
#方法1:agg函数传入字典,key是原列名,value是原列名和函数元组
df.groupby('MovieID').agg(
rating_mean=('Rating','mean'),
rating_min=('Rating','min'),
rating_max=('Rating','max'),
user_count=('UserID',lambda x :x.nunique())).head(1)
# 方法3:使用groupby之后apply对每个子df单独统计
def agg_func(x):
"""注意,这个x是子DF"""
# 这个Series会变成一行,字典KEY是列名
return pd.Series({
"rating_mean": x["Rating"].mean(),
"rating_min": x["Rating"].min(),
"rating_max": x["Rating"].max(),
"user_count": x["UserID"].nunique()
})
result = df.groupby("MovieID").apply(agg_func)
result.head(1)
分层索引:在一个轴向上拥有多个索引层级,可以表达更高维度数据的形式;
可以更方便的进行数据筛选,如果有序则性能更好;
groupby等操作的结果,如果是多KEY,结果是分层索引,需要会使用
一般不需要自己创建分层索引(MultiIndex有构造函数但一般不用)
Series: 有多层索引MultiIndex怎样筛选数据?
经过groupby 之后得到的都是series\
ser = stocks.groupby(['公司', '日期'])['收盘'].mean()
ser.head()
ser.loc['BIDU']
# 多层索引,可以用元组的形式筛选
ser.loc[('BIDU', '2019-10-02')]
DataFrame:多层索引MultiIndex
在选择数据时:\
元组(key1,key2)代表筛选多层索引,其中key1是索引第一级,key2是第二级,比如key1=JD, key2=2019-10-02
列表[key1,key2]代表同一层的多个KEY,其中key1和key2是并列的同级索引,比如key1=JD, key2=BIDU
stocks.set_index(['公司', '日期'], inplace=True)
stocks.head(1)
stocks.loc[('BIDU', '2019-10-02'), :]
stocks.loc[(['BIDU', 'JD'], '2019-10-03'), :]
stocks.loc[('BIDU', ['2019-10-02', '2019-10-03']), '收盘']
(注:reset_index还原分为两种类型,第一种是对原DataFrame进行reset,第二种是对使用过set_index()函数的DataFrame进行reset)
DataFrame.reset_index(level=None, drop=False, inplace=False, col_level=0, col_fill=”)\
level:int、str、tuple或list,默认无,仅从索引中删除给定级别。默认情况下移除所有级别。控制了具体要还原的那个等级的索引
drop:索引被还原成普通列后,是否删掉列。默认为False,为False时则索引列会被还原为普通列,否则被还原后的的列又会被瞬间删掉;
inplace:默认为false,适当修改DataFrame(不要创建新对象);
col_level:int或str,默认值为0,如果列有多个级别,则确定将标签插入到哪个级别。默认情况下,它将插入到第一级;
col_fill:对象,默认‘’,如果列有多个级别,则确定其他级别的命名方式。如果没有,则重复索引名;\
格式:DataFrame.set_index(keys, drop=True, append=False, inplace=False, verify_integrity=False)\
keys:列标签或列标签/数组列表,需要设置为索引的普通列
drop:是否删除原普通列,默认为True,删除用作新索引的原普通列;
append:是否变成复合索引,默认为False,即覆盖原索引,单索引;
inplace:默认为False,适当修改DataFrame(不要创建新对象);
verify_integrity:默认为false,检查新索引的副本。否则,请将检查推迟到必要时进行。将其设置为false将提高该方法的性能。\
stack:DataFrame.stack(level=-1, dropna=True),将column变成index,类似把横放的书籍变成竖放(脱帽)\
level=-1代表多层索引的最内层,可以通过==0、1、2指定多层索引的对应层
unstack:DataFrame.unstack(level=-1, fill_value=None),将index变成column,类似把竖放的书籍变成横放(戴帽)\
df.pivot_table(values=None, index=None, columns=None, aggfunc=‘mean’, fill_value=None, margins=False, dropna=True, margins_name=‘All’)
index :相当于行索引,如果赋值时列表,那么从左到右依次聚合,会自动合并第一列相同的值。
pd.pivot_table(df,index=['manager','status'],columns=['product'],values=['quantity','price'], aggfunc={
'quantity':len,'price':np.sum},fill_value=0)
数据转换函数对比:map、apply、applymap:
stocks = pd.read_excel('./datas/stocks/互联网公司股票.xlsx')
# 公司股票代码到中文的映射,注意这里是小写
dict_company_names = {
"bidu": "百度",
"baba": "阿里巴巴",
"iq": "爱奇艺",
"jd": "京东"
}
只用于Series,实现每个值->值的映射-series.map(dict) or series.map(function)传入字典/函数均可;
# 方法1:Series.map(dict) 传入字典
stocks["公司中文1"] = stocks["公司"].str.lower().map(dict_company_names)
# 方法2:Series.map(function) 传入函数
stocks["公司中文2"] = stocks["公司"].map(lambda x : dict_company_names[x.lower()])
用于Series和DataFrame的转换
不管用在series还是df上,第一个参数都是函数
Series.apply(function), 函数的参数是每个值
DataFrame.apply(function), 函数的参数是Series
apply:用于Series实现每个值的处理,用于Dataframe实现某个轴的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)
注意这个代码:
1、apply是在stocks这个DataFrame上调用;
2、lambda x的x是一个Series,因为指定了axis=1所以Seires的key是列名,可以用x[‘公司’]获取
只能用于DataFrame,用于处理该DataFrame的每个元素;
# 直接修改原df的这几列
sub_df = stocks[['收盘', '开盘', '高', '低', '交易量']]
stocks.loc[:, ['收盘', '开盘', '高', '低', '交易量']] = sub_df.applymap(lambda x : int(x))
知识:Pandas的GroupBy遵从split、apply、combine模式 。 这里的split指的是pandas的groupby,我们自己实现apply函数,apply返回的结果由pandas进行combine得到结果\
GroupBy.apply(function)\
function的第一个参数是dataframe
function的返回结果,可是dataframe、series、单个值,甚至和输入dataframe完全没关系
例1:演示:用户对电影评分的归一化\
每个用户的评分不同,有的乐观派评分高,有的悲观派评分低,按用户做归一化
# 实现按照用户ID分组,然后对其中一列归一化
def ratings_norm(df):
"""
@param df:每个用户分组的dataframe
"""
min_value = df["Rating"].min()# 每个分组后DF的Min
max_value = df["Rating"].max()# 每个分组后DF的Max
df["Rating_norm"] = df["Rating"].apply(
lambda x: (x-min_value)/(max_value-min_value))
return df
ratings = ratings.groupby("UserID").apply(ratings_norm)
ratings[ratings["UserID"]==1].head()
# 怎样取每个分组的TOPN数据? 获取2018年每个月温度最高的2天数据
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['month'] = df['ymd'].str[:7]
def getWenduTopN(df, topn):
"""
这里的df,是每个月份分组group的df
"""
return df.sort_values(by="bWendu")[["ymd", "bWendu"]][-topn:]
df.groupby("month").apply(getWenduTopN, topn=1).head()
Pandas日期处理的作用:将2018-01-01、1/1/2018等多种日期格式映射成统一的格式对象,在该对象上提供强大的功能支持\
几个概念:
pd.to_datetime:pandas的一个函数,能将字符串、列表、series变成日期形式
Timestamp:pandas表示日期的对象形式
DatetimeIndex:pandas表示日期的对象列表形式
其中:
DatetimeIndex是Timestamp的列表形式
pd.to_datetime对单个日期字符串处理会得到Timestamp
pd.to_datetime对日期字符串列表处理会得到DatetimeIndex
df.reset_index(inplace=True)
# 转变为标准的时间格式
df['day'] = pd.to_datetime(d2.loc[:,'ymd'],format='%Y/%m/%d ')
df2['day'] = df2['day'].values.astype('datetime64[D]')
df['month'] = df['day'].values.astype('datetime64[M]'
df2['opendays'] = (df2.day-df2.f_work_start_time)/np.timedelta64(1,'D')
# Timestamp、DatetimeIndex支持大量的属性可以获取日期分量(周数字、月数字、季度数字):
df['week_number']=df['day'].dt.week
df['month_number'] = df['day'].dt.month
df['quarter_mumber'] = df['day'].dt.quarter
# 通常把标准日期格式设置为索引,方便后续操作
df1.set_index('day',drop=True,inplace=True)
# 周数字列表
df.index.week ----- 和df['day'].dt.week 一样的效果
# 月数字列表
df.index.month
# 季度数字列表
df.index.quarter
1、设置图片大小
2、保存本地
3、描述信息,比如x轴y轴表示什么,这个图表示什么
4、调整x或者y的刻度的间距
5、线条的样式
6、标记出特殊的点
7、给图片添加一个水印
#设置图片大小
fig = plt.figure(figsize=(20,8),dpi=80)
x = range(2,26,2)
y = [15,13,14.5,17,20,25,26,26,24,22,18,15]
#绘图
plt.plot(x,y)
#设置x轴的刻度。通过改变x的步长可以调整x轴刻度的密集程度
# plt.xticks(x) 直接设置
# 如果设置的更密集一点。但是步长最小是1,不能是0。5
_xtick_labels = [i/2 for i in range(4,49)]
plt.xticks(_xtick_labels)
#设置y轴的刻度
plt.yticks(range(min(y),max(y)+1))
#展示图形
plt.show()
#问题1:如果列表a表示10点到12点的每一分钟的气温,如何绘制折线图观察每分钟气温的变化情况?
plt.figure(figsize=(20,8),dpi=80)
y=[random.randint(20,35) for i in range(120)]
x= range(0,120)
plt.plot(x,y)
#调整x轴的刻度(将整数换成字符串)
#_x = list(x)[::3]#强行将变量x转为列表,并每隔10个数字去一个值。方便坐标轴显示.或者直接在plt.xticks设置
_xtick_lables = ["10点{}分".format(i) for i in range(60)]
_xtick_lables += ["11点{}分".format(i) for i in range(60)]
# 取步长,数字和字符串一一对应,数据的长度一样
plt.xticks(list(x)[::3],_xtick_lables[::3],rotation=45)#旋转的度数
'''
1、让列表x中的数据和_xtick_lables的数据都传入,最终会在x轴上一一对应的显示出来
2、两组数据的长度必须一样,否则不能完全覆盖整个轴
3、使用列表的切片,每隔3个选一个数据进行显示
4、为了让字符串不会覆盖,使用rotation让字符串进行旋转
'''
#添加描述信息
plt.xlabel("时间")
plt.ylabel("温度/c")
plt.title("10点到12点每分钟气温变化的情况")
plt.show()
#练习题:假设大家在30岁的时候根据自己的实际情况,统计出来了从11岁到30岁每年交的那男女朋友的数量如列表a,
#请绘制出该数据的折线图以便分析数量走势。a=[1,0,1,1,2,4,3,2,3,4,4,5,6,5,4,3,3,1,1,1]要求:x轴表示岁数,如11岁。 y轴表示个数
plt.figure(figsize=(15,5),dpi=400)
y=[1,0,1,1,2,4,3,2,3,4,4,5,6,5,4,3,3,1,1,1]
x=list(range(11,31))
plt.plot(x,y)
#设置x轴坐标显示
_xtick_lables = ["{}岁".format(i) for i in x]
plt.xticks(x[::2],_xtick_lables[::2])
plt.yticks(range(0,9))
#添加描述信息
plt.xlabel("岁数")
plt.ylabel("朋友个数")
plt.title("11-30岁交往的朋友个数")
plt.grid(alpha=0.4)#显示网格
plt.show()
plt.figure(figsize=(15,5),dpi=80)
y1=[1,0,1,1,2,4,3,2,3,4,4,5,6,5,4,3,3,1,1,1]
y2=[1,0,3,1,2,2,3,3,2,1,1,1,1,1,1,1,1,1,1,1]
x=list(range(11,31))
plt.plot(x,y1,label="自己",color='red',marker = 'o')
plt.plot(x,y2,label="同桌",color='cyan',linestyle=":")
#设置x轴坐标显示
_xtick_lables = ["{}岁".format(i) for i in x]
plt.xticks(x[::2],_xtick_lables[::2])
#plt.yticks(range(0,9))
#添加描述信息
plt.xlabel("岁数")
plt.ylabel("朋友个数")
plt.title("11-30岁交往的朋友个数")
plt.grid(alpha=0.4)#显示网格
plt.legend(loc='upper left')#显示图例
plt.show()