对DataFrame其中一列进行逻辑计算,会产生一个对应的由布尔值组成的Series
in:
df.Q1>36
out:
0 True
1 False
2 True
3 True
4 True
...
95 True
96 False
97 True
98 False
99 False
Name: Q1, Length: 100, dtype: bool
也可针对索引进行逻辑判断
in:
df.index ==1
out:
array([False, True, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False])
in:
df.loc[:,'Q1':'Q4']>60
out:
Q1 Q2 Q3 Q4
0 True False False True
1 False False False False
2 False False False True
3 True True True True
4 True False True True
... ... ... ... ...
95 False False True True
96 False False False False
97 True True False False
98 False True False True
99 False False False True
100 rows × 4 columns
除了支持逻辑运算,Pandas还支持组合条件的Python位运算
in:
#Q1成绩不小于60分,并且是C组成员
~(df.Q1 < 60) & (df['team'] == 'C')
out:
0 False
1 False
2 False
3 True
4 False
...
95 False
96 False
97 True
98 False
99 False
Length: 100, dtype: bool
示例:
df[df['Q1'] == 8]
df[~(df['Q1'] == 8)]
df[df.name == 'Ben']
df[df.Q1>df.Q2]
df.loc[df['Q1'] > 90,'Q1':]
df.loc[(df.Q1>80)&(df.Q2<15)]
df.loc[(df.Q1>90)|(df.Q2<90)]
df.loc[df['Q1'] == 8]
需要注意的是在进行 或(|)、 与(&) 、 非(~) 运算时,各个独立逻辑表达式需要用括号括起来
any 和 all对逻辑计算后的布尔序列再进行判断,序列中所有值都为True时all才返回True,序列中只要有一个值位True时any就返回True。还可以传入axis参数,来指定判断方向
#Q1,Q2成绩全为超过80分的
df[(df.loc[:,['Q1','Q2']] > 80).all(1)]
#Q1,Q2成绩至少有一个超过80的
df[(df.loc[:,['Q1','Q2']]>80).any(1)
上述对两个列整体先做逻辑计算得到一个两列的布尔序列,然后用all和any在行方向上做逻辑运算
可以在表达式处使用lambda函数,默认变量是其操作的对象。如果操作的对象是一个DataFrame,那么变量就是这个DataFrame,如果是一个Series,那么就是这个Series
in:
s = df.Q1
#查询最大索引的值
df.Q1[lambda s: max(s.index)]
out:
21
in:
#计算最大值
max(df.Q1.index)
对Pandas的一些比较函数,使我们可以将逻辑表达式替换为函数形式
#以下相当于df[df.Q1 == 60]
df[df.Q1.eq(60)]
除了.eq(),还有:
df.ne() #不等于!=
df.le() #小于等于 <=
df.lt() #小于<
df.ge() #大于等于>=
df.gt() #大于>
实例:
df[df.Q1.ne(89)] #Q1不等于89
df.loc[df.Q1.gt(90) & df.Q2.lt(90)] #and关系,Q1>90, Q2<90
这些函数可以传入一个定值、数列、布尔序列、Series或DataFrame,来与原数据比较
另外还有一个.isin()函数,用于判断数据是否包含指定内容。可以传入一个列表,原数据只要满足其中一个存在即可,也可以传入一个字典,键为列名,值为需要匹配的值,以实现按列个性化匹配存在值
df[df.team.isin(['A','B'])]
df[df.isin({'team':['C','D'],'Q1':[36,93]})]#复杂查询,其他值为NaN
df.query(expr)使用布尔表达式查询DataFrame的列,表达式是一个字符串
df.query('Q1>Q2>90')
df.query('Q1+Q2>180')
df.query('(Q1<50)&(Q2>40) and (Q3>90)')
df.query('team not in ("E","A","B")')
#对于名称中带有空格的列,可以使用反引号引起来
df.query('B == `team name`')
还支持用@符号引入变量
a = df.Q1.mean()
df.query('Q1>@a+40')
df.query('Q1>`Q2`+@a')
df.eval()和df.query()类似,也可以用于表达式筛选
df[df.eval("Q1>90>Q3>10")]
df[df.eval("Q1>`Q2`+@a")]
df.filter()可以对行名和列名进行筛选,支持模糊匹配、正则表达式
df.filter(items = ['Q1','Q2']) #选择两列
df.filter(regex = 'Q',axis = 1) #列名包含Q的列
df.filter(regex = 'e$',axis = 1) #以e结尾的列
df.filter(like = '2',axis = 0) #索引中有2的
df.filter(regex = '^2',axis = 0).filter(like = 'Q',axis =1) #索引中以2开头,列名有Q的
df.select_dtypes(include = None,exclude = None)
可以指定包含和不包含的数据类型,如果只有一个类型,传入字符,如果有多个类型,传入列表
df.select_dtypes(include = ['float64'])
df.select_dtypes(exclude = ['int'])
若没有满足条件的数据,则返回一个仅有索引的DataFrame
加载数据时指定数据各列的类型:
#对所有字段指定同一类型
df = pd.DataFrame(data,dtype = 'float32')
#对每个字段分别指定
df = pd.read_excel(data,dtype = {'team':'string','Q1':'int32'})
2.1推断类型
Pandas可以用以下方法智能地推断各列的数据类型,会返回一个按推断修改后的DataFrame。如果需要使用这些类型的数据,可以赋值替换
#自动转换合适的数据类型
df.infer_objects() #推断后的DataFrame
df.infer_objects().dtypes
out:
name team Q1 Q2 Q3 Q4
0 Liver E 89 21 24 64
1 Arry C 36 37 37 57
2 Ack A 57 60 18 84
3 Eorge C 93 96 71 78
4 Oah D 65 49 61 86
... ... ... ... ... ... ...
95 Gabriel C 48 59 87 74
96 Austin7 C 21 31 30 43
97 Lincoln4 C 98 93 1 20
98 Eli E 11 74 58 91
99 Ben E 21 43 41 74
100 rows × 6 columns
name object
team object
Q1 int64
Q2 int64
Q3 int64
Q4 int64
dtype: object
#推荐这个新方法,支持string类型
df.convert_dtypes() #推断后的DataFrame
df.convert_dtypes().dtypes
out:
name team Q1 Q2 Q3 Q4
0 Liver E 89 21 24 64
1 Arry C 36 37 37 57
2 Ack A 57 60 18 84
3 Eorge C 93 96 71 78
4 Oah D 65 49 61 86
... ... ... ... ... ... ...
95 Gabriel C 48 59 87 74
96 Austin7 C 21 31 30 43
97 Lincoln4 C 98 93 1 20
98 Eli E 11 74 58 91
99 Ben E 21 43 41 74
100 rows × 6 columns
name string
team string
Q1 Int64
Q2 Int64
Q3 Int64
Q4 Int64
dtype: object
pd.to_xxx 系统方法可以将数据安全转换,errors参数可以实现无法转换则转换为兜底类型:
#按大体类型推断
m = ['1',2,3]
s = pd.to_numeric(s)#转成数字
pd.to_datetime(m) #转成时间
pd.to_timedelta #转成时间差
pd.to_datetime(m,errors = 'coerce') #错误处理
pd.to_numeric(m,errors = 'coerce').fillna(0) #兜底填充
pd.to_datetime(df[['year','month','day']]) #组合成日期
转换成数字类型时,默认返回的dtype使float64还是int64取决于提供的数据,使用downcast参数获得向下转换后的其他类型
#最低期望
pd.to_numeric(m,downcast = 'integer') #至少为有符号int数据类型
pd.to_numeric(m,downcast = 'float') #至少为float浮点类型
df.Q1.astype('int32')
pd.to_datetime()和s.astype('datetime64[ns]')来做时间类型转换
df.sort_index()实现按索引排序,默认以从小到大的升序方式排列。如果希望按降序排序,传入ascending = False。可以传入axis = 1来列索引方向上排序。
若想改变原数据,传入inplace = True
参数:na_position = 'first' 表示空值在前,如果是last表示空值在后
传入level = 1,表示如果有多层,排一级
df.reindex()指定自己定义顺序的索引,实现行和列的顺序重新定义:
df = pd.DataFrame({
'A':[1,2,3],
'B':[4,5,6]
},index = ['a','b','c'])
#按要求重新指定索引顺序
df.reindex(['c','b','a'])
#指定列顺序
df.reindex(['B','A'],axis = 1)
数值排序主要使用sort_values()
数字按大小顺序,字符按字母顺序
Series和DataFrame都支持此方法
df.Q1.sort_values()
df.sort_values('Q4')
默认升序,但可以指定排序方式
df.sort_values(by = ['team','name'],ascending = [True,False])
其他参数:
ascending = True/False
inplace = True/False
na_position = 'first'/'last'
df.sort_values(by = ['xx']) 按指定字段排列
df.sort_values('Q1')
df.sort_values('team',ignore_index = True) #索引重新0-(n-1)排
有时需要使用索引和数据值混合排序,需要先给索引起名,再进行排序
df.set_index('name',inplace = True) #设置name为索引
df.index.names = ['s_name'] #给索引起名
df.sort_values(by = ['s_name','team']) #排序
nsmallest() 和nlargest()用来实现数字列的排序,并可以指定返回的个数
df.nsmallest(5,['Q1','Q2']) #先按Q1最小在前,如果相同,Q2小的在前
以上显示前五个最小的数,仅支持数字类型的排序
# s是一组series数据
s.nsmallest(3) #最小的3个
修改数据
先筛选出需要修改的数值范围,再为这个范围重新赋值
#将小于60分的成绩修改为60
df[df.Q1<60] = 60
替换数据
replace方法
df.replace(0,5) #将数据中所有的0换为5
df.replace([0,1,2,3],4) #将0-3换为4
df.replace([0,1,2,3],[4,3,2,1]) #对应修改
s.replace([1,2],method = 'bfill') #向下填充
df.replace({0:10,1:100})
df.replace({'Q1':0,'Q2':5},100) #将指定字段的指定值修改为100
在传递值时,也可以使用正则表达式
填充缺失值
df.fillna(0) #将空值全修改为0
df.fillna(method = 'ffill') #将空值都修改为前一个值
values = {'A':0,'B':1,'C':2}
df.fillna(value = values) #为各列填充不同的值
df.fillna(value = values,limit) #只替换第一个
修改索引名
最常用的方法是df.index和df.columns重新赋值为一个类似于列表的序列值,这会将其覆盖为指定序列中的名称。
使用df.rename和df.rename_axis对轴名称进行修改
df.rename(columns = {'team':'class'}) #将team修改为class
插入列
df.insert()
insert()方法可以传入三个主要参数:loc是一个数字,表示新列所在的位置,使用列的数组索引,第二个参数column为新的列名,最后一个参数value为列的值,一般是一个series
df.insert(2,'total',df.sum(1)) #值为每行的总成绩
如果已经存在相同的数据列,会报错,可以传入allow_duplicates = True插入一个同名的列
指定列df.assign()
df.assign(k=v)为指定一个新列的操作,k为新列的列名,v为此列的值,v必须是一个与原数据同索引的Series。
这个方法在链式编程技术中比较重要。
我们平时在做数据探索分析时会增加一些临时列,如果新列全部使用赋值的方式生成,则会造成原数据混乱,因此需要一个方法来让我们不用赋值也可以创建一个临时的列。这种思路也适用于所有对原数据的操作,建议在未最终确定数据处理方案时,除了必要的数据整理工作,均使用链式方法。
#增加total列
df.assign(total = df.sum(1))
#原数据没有变化
#增加两列
df.assign(total = df.sum(1),Q = 100) #Q这一列的值均为100
df.assign(total = df.sum(1)).assign(Q=100) #效果同上
#再增加两列name_len和avg
#使用链式方法:
(
df.assign(total = df.sum(1))#总成绩
.assign(Q = 100) #目标满分值
.assign(name_len = df.name.str.len())
.assign(avg = df.mean(1)) #平均值
.assign(avg2 = lambda d:d.total/4) #平均值2
)
要特别说明的是avg2列,因为df实际是没有total这一列的,如果我们需要使用total列,就需要使用lambda来调用。lambda中的第一个参数d是代码执行到本行前的DataFrame内容,可以认为是一个虚拟的DataFrame实体,然后用变量d使用这个DataFrame的数据。这个d是变量名,可以任意命名
执行表达式df.eval()
df.eval()与之前介绍的df.query()一样,可以以字符的形式传入表达式,增加列数据。下面以增加总分为例
df.eval('total = Q1+Q2+Q3+Q4')
df['C1'] = df.eval('Q2+Q3')
a = df.Q1.mean()
df.eval("C3 = `Q3`+@a")
df.eval("C3 = Q2>(`Q3`+@a)")
df.append()可以追加一个新行
#追加合并
df = pd.DataFrame([[1,2],[3,4,]],columns = list('AB'))
df2 = pd.DataFrame([[5,6],[7,8]],columns = list('AB'))
df.append(df2)
追加合并
df.append()可以追加一个新行
pd.concat([s1,s2])可以将两个df或s连接起来:
s1 = pd.Series(['a','b'])
s2 = pd.Series(['c','d'])
pd.concat([s1,s2])
pd.concat([s1,s2],ignore_index = True)
删除
删除有两种方法:一种是pop()函数,使用pop(),Series会删除指定索引的数据并同时返回这个被删除的值,DataFrame会删除指定列并返回这个被删除的列。
还有一种是反选法,将需要的数据筛选出来赋值给原变量,最终实现删除
df.where()中可以传入一个布尔表达式、布尔值的Series/DataFrame、序列或者可以调用的对象,然后与原数据做对比,返回一个行索引与列索引与原数据相同的数据,且在满足条件的位置保留原值,在不满足条件的位置填充NaN
df = df.select_dtypes(include = 'number')
df.where(df>70)
传入一个可调用对象,比如lambda
df.where(lambda d:d.Q1>50)
可以指定将不满足条件的值替换为指定值
#大于等于60分的显示成绩,小于的显示“不及格”
df.where(df>=60,'不及格')
可以传入一个算法
#c:定义一个数是否为偶数的表达式
c = df%2 == 0
#传入c,为偶数时显示原值减去20后的相反数
df.where(~c,-(df-20))
df.where()方法可以将满足条件的值筛选出来,将不满足的值替换为另一个值,但无法对满足条件的值进行替换。np.where()可以
#小于60分为不及格
np.where(df>=60,'合格','不及格') #返回的是一个二维array
#让df.where()中的条件为假,从而应用np.where()的计算结果
df.where(df == 65164815165,np.where(df>=60,'合格','不合格'))
(
df.assign(avg = df.mean(1)) #计算一个平均数
.assign(及格 = lambda d:np.where(d.avg>=60,'是','否')) #通过np.where()判断平均分是否及格
)
df.mask()的用法跟df.where()基本相同,唯一的区别是df.mask()将满足条件的位置填充为NaN,也可以指定填充值
语法:df.lookup(行标签,列标签),返回一个numpy.ndarrray,标签必须是一个序列
in:
#行列相同数量,返回一个array
df.lookup([1,3,4],['Q1','Q2','Q3'])
out:
array([36, 96, 61], dtype=int64)
大多数据迭代用for
迭代索引和指定的多列,使用python内置的zip函数将其打包成可迭代的zip对象
#迭代索引和指定的两列
for i,n,q in zip(df.index,df.name,df.Q1):
print(i,n,q)
df.iterrows()生成一个可迭代对象,将DataFrame行作为(索引,行数据)组成的Series数据进行迭代。在for语句中需要两个变量来承接数据:一个作为索引变量,即使索引在迭代中不会使用(这种情况可以使用useless来作为变量名);另一个为数据变量,读取具体列时,可以使用字典的方法和对象属性的方法。
此段代码作用同上
#迭代,使用name,Q1数据
for index,row in df.iterrows():
print(index,row['name'],row.Q1)
df.iterrows()是最常用、最方便的按行迭代方法
df.itertuples()生成一个nametuples类型数据,name默认名为Pandas,可以在参数中指定
in:
for row in df.itertuples():
print(row)
out:
Pandas(Index=0, name='Liver', team='E', Q1=89, Q2=21, Q3=24, Q4=64)
Pandas(Index=1, name='Arry', team='C', Q1=36, Q2=37, Q3=37, Q4=57)
Pandas(Index=2, name='Ack', team='A', Q1=57, Q2=60, Q3=18, Q4=84)
Pandas(Index=3, name='Eorge', team='C', Q1=93, Q2=96, Q3=71, Q4=78)
Pandas(Index=4, name='Oah', team='D', Q1=65, Q2=49, Q3=61, Q4=86)
Pandas(Index=5, name='Harlie', team='C', Q1=24, Q2=13, Q3=87, Q4=43)
......
for row in df.itertuples(index = False,name = 'Gairuo'):
print(row)
print(row.Index,row.name)
df.items()和dfiteritems()功能相同,它迭代时返回一个(列名,本列的Series结构数据),实现对列的迭代
in:
#Series取前三个
for label,ser in df.items():
print(label)
print(ser[:3],end = '\n\n')
out:
name
0 Liver
1 Arry
2 Ack
Name: name, dtype: object
team
0 E
1 C
2 A
Name: team, dtype: object
Q1
0 89
1 36
2 57
Name: Q1, dtype: int64
Q2
0 21
1 37
2 60
Name: Q2, dtype: int64
Q3
0 24
1 37
2 18
Name: Q3, dtype: int64
Q4
0 64
1 57
2 84
Name: Q4, dtype: int64
如果需要对Series的数据再进行迭代,可嵌套for循环
除了df.items(),如果需要迭代一个DataFrame的列,可以直接对DataFrame迭代,会循环得到列名
#直接对DataFrame迭代:
for column in df:
print(column)
#依次取出每个列
for column in df:
print(df[column])
#可对每个列的内容进行迭代
for column in df:
for i in df[column]:
print(i)
#迭代指定列
for i in df.name:
print(i)
#只迭代想要的列
l = ['name','Q1']
cols = df.columns.intersection(l)
for col in cols:
print(col)
pipe()叫做管道方法。可以让我们写的分析过程标准化、流水线化,达到复用目标。
DataFrame和Series都支持pipe()方法。语法结构为:
df.pipe(函数名,传给函数的参数列表或字典)
它将DataFrame或Series作为函数的第一个参数,可以根据需求返回自己定义的任意类型数据
pipe可以将复杂的调用简化
#对df多重应用多个函数
f(g(h(df),arg1 = a),arg2 = b,arg3 = c)
#arg是传入的参数
#用pipe()可以将它们连接起来
(
df.pipe(h)
.pipe(g,arg1 = a)
.pipe(f,arg2 = b,arg3 = c)
)
#以下是将arg2参数传给函数f,然后作为函数整体接受后面的参数
(
df.pipe(h)
.pipe(g,arg1 = a)
.pipe((f,'arg2'),arg1 = a,arg3 = c)
)
实例:
#定义一个函数,给所有季度的成绩加n,然后增加平均数
#其中n中要加的值为必传参数
def add_mean(rdf,n):
df = rdf.copy()
df = df.loc[:,'Q1':'Q4'].applymap(lambda x:x+n)
df['avg'] = df.loc[:,'Q1':'Q4'].mean(1)
return df
#调用
df.pipe(add_mean,100) #将100作为参数传入函数add_mean
下例完成了一个数据筛选需求,lambda的第一个参数为self,即使用前的数据本身,后面的参数可以在逻辑代码中使用
#筛选出Q1大于等于80且Q2大于等于90的数据
df.pipe(lambda df_,x,y:
df_[(df_.Q1 >= x) & (df.Q2 >= y)],80,90)
apply()可以对DataFrame按行和列(默认)进行函数处理,也支持Series。如果是Series,逐个传入具体值,DataFrame逐行或逐列传入
#将文本全部转换为全小写
#将name全部变为小写
df.name.apply(lambda x: x.lower())
#去掉一个最高分和最低分再算出平均分
def my_mean(s):
max_min_ser = pd.Series([-s.max(),-s.min()])
return s.append(max_min_ser).sum()/(s.count()-2)
#对数字列应用函数
df.select_dtypes(include = 'number').apply(my_mean)
这段代码里,函数my_mean接收一个Series,从此Series中取最大值和最小值的负值组成一个需要减去的负值Series,传入的Series追加词负值,最后求和,并取平均值
df.applymap()可以实现元素级函数应用,即对DataFrame中所有的元素(不包括索引)应用函数处理
#计算数据的长度
def mylen(x):
return len(str(x))
df.applymap(lambda x:mylen(x)) #应用函数
df.applymap(mylen) #效果同上
map()根据输入对应关系映射值返回最终数据,用于Series对象或DataFrame对象的一列。传入的值可以是一个字典,键为原数据值,值为替换后的值。
可以传入一个函数(参数为Series的每个值),还可以传入一个字符格式化表达式来格式化数据内容
df.team.map({'A':'一班','B':'二班','C':'三班','D':'四班'}) #枚举替换
#每列的最大值
df.agg('max')
#将所有列聚合产生sum和min两行
df.agg(['sum','min'])
#序列多个聚合
df.agg({'Q1':['sum','min'],'Q2':['min','max']})
#分组后聚合
df.groupby('team').agg('max')
df.Q1.agg(['sum','mean'])
agg()还支持传入函数的位置参数和关键字参数,支持每个列分别用不同的方法聚合,支持指定轴的方向
DataFrame和Series自身调用函数并返回一个与自身长度相同的数据
如:df.transform(lambda x:x*2)