1-Nump和Pandas教程

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
# 全部行,每行都输出

1.Numpy

Numpy 是数据分析包的基础,提供高性能数组与矩阵的运算能力,主要用来处理数值型数据。 -ndarray:数组。

数组中的所有元素,必须类型统一。

1.1 numpy基础

import numpy as np
arr1=np.array([1,2,3]) # 列表创建数组
arr2=np.array((1,2,3)) # 元组创建数组

type(arr1) # numpy.ndarray 类型

np.zeros(8) #zeros 创建全0数组
np.ones(10) #创建全1数组
np.zeros((2,3)) # 创建2行3列全0数组,ones同理 
np.full((3,3),True) # 3行3列全部值为True
np.empty((2,3)) # 创建空数组 
np.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),类似range

#ones_like   zeros_like   empty_like 生成目标array尺寸一样的全1/0/空矩阵
arr3 = np.arange(12).reshape(3,4)
arr_one=np.ones_like(arr3)

np.eye(3,5) # 生成单位对角矩阵
np.identity(5) # 生成对角方阵

np.random.randint(0,10,size=(2,3)) # 随机生成2行3列的整数
np.random.rand(2,3) # 随机生成2行3列的服从“0~1”均匀分布的随机样本值。随机样本取值范围是[0,1)
np.random.randn(2,3) # 随机生成2行3列的服从正太分布的样本,平均值为0,标准差为1的样本值
np.random.normal(2,1,size=(3,5)) # 生成平均值2,标准差为1的正太分布数组
np.random.seed(0) # 随机数种子,固定的随机
np.random.normal(2,1,size=(3,5)) # 设定随机数种子后,每次生成的一样
np.linspace(-1,1, 100) # 创建-1到1的等差数列,100个值
              
                                      
arr4=np.random.normal(3,4,size=(3,5,6,7))
arr4.ndim # 查看维度
arr4.shape # 数组维度的元祖  (3, 5, 6, 7)
arr4.size # 数组元素的个数 630
arr4.dtype # 数据类型 dtype('float64')
arr4.itemsize # 每个数组元素的字节大小 8
np.isnan(arr4) # 检查是否为空,会输出该元祖,原来位置的元素替换成False或者True

arr5 = np.arange(0,5,dtype="float64") # array([0., 1., 2., 3., 4.])
arr5.astype('string_') # 创建新的数组,不改变原本的数组,array([b'0.0', b'1.0', b'2.0', b'3.0', b'4.0'], dtype='|S32')
arr5.dtype # dtype('float64')
arr5.astype(np.float) #转换数据类型,有int float64 float32 bool string_

arr6 = np.arange(24) # 一维
arr6.reshape(3,8) # 二维,数组重塑reshape
arr6.reshape(2,3,4) # 变成三维数组

# 数组的合并
arr1=np.arange(0,5) # 二维时向下为0轴,向右为1轴
arr2=np.arange(0,5)
np.vstack((arr1,arr2)) # 纵向合并vertical 轴 0
np.hstack((arr1,arr2)) # 横向合并horizontal 轴1,array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4])

arr1=np.arange(0,6).reshape(2,3)
arr2=np.arange(0,6).reshape(2,3) # 三维时向下为1轴,向右为2轴,向里为0轴
arr7 = np.arange(24).reshape(2,3,4) # 2片3行4列
arr8 = np.arange(24).reshape(2,3,4)
c=np.concatenate([arr7,arr8],axis=0)
c.shape # (4, 3, 4),4片3行4列
d=np.concatenate([arr7,arr8],axis=1)
d.shape # (2, 6, 4),2片6行4列
e=np.concatenate([arr7,arr8],axis=2)
e.shape # (2, 3, 8),2片3行8列

brr1=np.arange(6).reshape(2,3).T # 转置,变成3行2列

brr1+brr1 # 数组形状不同而相加时会自动广播
brr1*brr1
brr1-brr1
1/brr1
brr1**2
brr1>brr1 # 数组对应元素相比较

arr1[0] # 数组索引
arr1[:3] # 数组切片

arr2=np.arange(1,7).reshape(2,3)
arr2[0] # 取到第一行元素
arr2[0,1] # 取到0行下标为1的元素
arr2[1,1:] # 在第1行中1下标开始切片,array([5, 6])

# 布尔索引
arr2[arr2>3] # array([4, 5, 6])
arr2[arr2>3]=5 # 数组中大于3的元素全替换为5

np.exp(brr1) # 自然指数
np.log10(brr1)
np.sign(brr1) # 计算正负号,三角函数 sin cos tan   
np.square(arr1) # 平方
np.dot(a,b) # 数学矩阵的乘法
np.matmul(a,b) # 数学矩阵的乘法,禁止矩阵与标量的乘法
np.add(arr7,arr8) # 矩阵对应位置相加

# 条件逻辑
yes = np.ones(10)
no = np.zeros(10)
cond = np.array([True,False,True,True,True,False,False,False,True,True])
np.where(cond,yes,no) # array([1., 0., 1., 1., 1., 0., 0., 0., 1., 1.])

np.where(array!=0) # 找出非0元素的位置索引,array([0, 1, 4], dtype=int64
array[array%2!=0] # 提取所有的奇数
array[array%2!=0]=-1 # 所有奇数替换为-1
np.arange(10).reshape((2,-1)) # 转换为2行的二维数组

a=np.ones(shape=(10,10),dtype=np.int8)
a[1:-1,1:-1]=0 # 创建一个10*10的ndarray对象,且矩阵边界全为1,里面全为0

np.where(a==b) # 获取2数组对应位置相等的索引,array([1, 3, 5, 7], dtype=int64

array[(array>=5)&(array<=10)] # 提取数组中5到10之间的所有数字

np.arange(2,3*10+2,3) # 创建起点2,长度10和步长3的数组

# 统计学:sum mean std min max argmin位置
a=np.arange(24).reshape(2,3,4)
a.sum(axis=0) # 2片二维数组叠加求和
a.sum(axis=1) # 向下求和,2行4列
a.sum(axis=2) # 向右求和,2行3列

1.2 numpy进阶

data = np.loadtxt("orderinfo.csv",delimiter = ',',usecols=(range(0,4))
                  ,unpack = True  #分列读取
                 ) # loadtxt 读取

np.sum(counts[years==y]*price[years==y]) # 计算某年订单总额
np.sum(counts[years==y]*price[years==y])/sum(counts[years==y]) # 计算某年的平均订单额
np.sum(counts[(months==m)&(years==y)]*price[(months==m)&(years==y)]) # 计算某一年某一月的订单额
np.sum(counts[months==m]*price[months==m])/np.sum(counts[months==m]) # 计算某个月的平均订单额(年未分开)

import matplotlib.pyplot as plt
guanyu = plt.imread(r'guanyu.jpg') # 读取图像,返回图像的数组(三维数组)。
plt.imshow(guanyu) # 显示图像
plt.imshow(np.max(guanyu,axis=2))  # 取出[R,G,B]里面最大的值
arr1[:,:20,:]=0 # 图像加黑边,依次代表行,列,片
guanyu_doublekill=np.concatenate((guanyu,guanyu),axis=0) # 关羽变x2,关羽沿着0轴(竖向)方向,拼接,1轴是横向,2轴会报错
plt.imsave() # 保存图像

data = np.loadtxt("stock.csv",delimiter = ',',usecols=(range(3,8)),unpack = True  #分列读取
                 ) # loadtxt 读取
data.shape # (5, 30)

start_price = data[0] #开盘价
max_price = data[1] # 最高价
min_price = data[2] # 最低价
end_price = data[3] # 收盘价
turnover = data[4] # 成交量

np.sum(end_price)/np.size(end_price) # 不加权重
np.mean(data[3])
np.average(end_price) # 同上
np.sum(end_price*turnover)/np.sum(turnover) # 成交量加权的收盘平均价格
np.average(end_price,weights=turnover) # 同上

np.max(data,axis = 1) # 计算最大值
np.ptp(min_price) # 计算极差
np.var(data, axis = 1) # 计算方差

2.Pandas

2.1 pandas入门

2.2.1 Series

类似于一维数组对象,Value:一维数组,Index:索引。

s1 = Series(data=['发顺丰','SDFS','G4FS']) # 默认索引是0,1,2
list1 = ["Jay","JJ","JACK","Gai"]
s1 = Series(data=list1,index = ["A","B","C"]) # 指定索引
s1.values # 只取值,['发顺丰','SDFS','G4FS'],dtype=object
s1.index # 只取索引,["A","B","C"],dtype=object

arr=np.arange(0,16).reshape(2,8)
s2=Series(data=arr) # 报错,必须是一维,arr1=arr.flatten()推平即可

s2=Series(data=arr1,index = np.arange(0,16)[::-1])

s2[s2>10] # 筛选value
s2*2 # value*2
s2-3 # 每个元素减3,自动广播
s2[10]  # 5

s3=Series({'吕布':'高富帅' ,'程咬金':'小墩墩','潘田':'仙女'}) # 由字典创建
s3.name='list' # 定义name,相当于值的列名
s3.index = ['A','B','C'] # 更改index
s3['A'] = 'hehe' # 利用index更改值,不能这样s3.values=['dd','aa','as']改值,报错

2.1.2 DataFrame

是一种表格型数据结构,它含有一组有序的列,每列可以是不同的值。DataFrame既有行索引,也有列索引,它可以看作是由Series组成的字典,不过这些Series公用一个索引。 DataFrame的创建有多种方式,不过最重要的还是根据dict进行创建,以及读取csv、txt、excel文件来创建。DataFrame的一列相当于一个Series。

data={'country':['CHINA','CHINA1','CHINA2','CHINA3'],
      'city':['Chengdu','chongqing','kuerle','beijing'],
      'people':[800,650,30,2000]}
df=DataFrame(data)  # 通过字典创建df

df.values # 取df的值

df['country'] # 取国家列,取某一列或某几列包含有index
df.country # 同上

li = [[1, 2, 3, 4],
    [2, 3, 4, 5]]
# DataFRame对象里面包含两个索引, 行索引(0轴,axis=0), 列索引(1轴,axis=1)
data.to_csv(r"文件名.csv") #   保存成CSV
df.to_excel('test.xlsx') #    保存成Excel
d1 = pd.DataFrame(data=li, index=['A', 'B'], columns=['views', 'loves', 'comments', 'tranfers']) #通过列表创建

# 多个df写入到一个Excel文件中
writer = pd.ExcelWriter('大众汽车.xlsx')
df1.to_excel(writer,"配置")
df2.to_excel(writer,"外观")
writer.save()

with pd.ExcelWriter('大众汽车.xlsx') as writer:
	df1.to_excel(writer,sheet_name="配置",index=False)
	df2.to_excel(writer,sheet_name="外观",index=False)
	
narr = np.arange(8).reshape(2, 4) #创建二维四列的数组
d2 = pd.DataFrame(data=narr, index=['A', 'B'], columns=['views', 'loves', 'comments', 'tranfers']) #通过numpy对象创建

d2.shape  # 获取行数和列数;
d2.dtypes  # 列数据类型
d2.ndim  # 获取数据的维度
d2.info() # 获取数据类型,内存占用
d2.describe() # 获取描述性统计
d2.index # 行索引
d2.columns # 列索引
df['A'].tolist() # 某列的值转换成列表
df.set_index('SKU')['售价'].to_dict() # 某两列转换成字典
print(d2.values, type(d2.values))   # 对象的值,class 'numpy.ndarray'

df=pd.read_csv('Script.csv') # 读csv文件,注意编码
df.head() # 查看前面几行,默认5行
df.tail() # 查看最后几行
df.drop([len(df)-1],inplace=True) # 删除最后一行数据
df.drop(['A', 'B'],axis=1,inplace=True) # 删除列
df = df.append({'grammer':'Perl','popularity':6.6},ignore_index=True) # 添加一行,后续pandas版本会弃用

df['Name'].nunique() # 某列数据统计去重后的个数
df['Name'].unique() # 某列数据统计去重后值
df['Name'].value_counts().index[0] # 谁说了最多的话

# 时间相关
df['purchase_date'] = pd.to_datetime(df['purchase_date']) # 转换成时间格式
df['year'] = df['purchase_date'].dt.year # 转换后.dt相当于datetime对象
df['weekofyear'] = df['purchase_date'].dt.weekofyear # 一年的第几周
df['month'] = df['purchase_date'].dt.month # 获取月份
df['dayofweek'] = df['purchase_date'].dt.dayofweek # 一周的第几天,周一是0
df['weekend'] = (df.purchase_date.dt.weekday >=5).astype(int)
df['hour'] = df['purchase_date'].dt.hour # 小时
df['day'] = df['purchase_date'].dt.day # 天 
df['day'] = df['purchase_date'].dt.day_name() # Friday
df[df.dt.quarter > 2] # 筛选下半年
df[df.is_year_end] # 筛选每年12-31日

df['addr'].str.split('-') # 分列
df['addr'].str.upper() # 大写
df['addr'].str.count(r'\d') # 数字计数
df['addr'].str.replace('.', '').str.extract(r'\d+')

Series对象和DataFrame的列数据提供了cat、dt、str三种属性接口(accessors),分别对应分类数据、日期时间数据和字符串数据

2.2 pandas索引

2.2.1 loc 标签索引,行列都支持<切片,列表>

loc方法,通过索引(行索引,列索引)来查找,比如ID,和列名

df = pd.read_csv('data/table.csv',index_col='ID')

# 行索引
df.loc[32] # 取第33行,没有逗号默认是取行索引,没有逗号就是对行操作
df.loc[[1101,1102]] # 取两行,用一个列表
df.loc[1101:1105] # 切片取,用分号
df.loc[1101:1105:2] # 切片


# 列索引
df.loc[:,'Height'] # 单列索引
df.loc[:,['Height','Math']] # 多列索引
df.loc[:,'Height':'Math'] # 切片的多列索引

# 联合索引
df.loc[1101:1105:2,'School':'Address'] # 联合索引,用逗号隔开,都在切片

# 函数索引
df.loc[lambda x:x['Gender']=='M'] #loc中使用的函数,传入参数就是前面的df
def f(x):
    return [1101,1103]
df.loc[f] # loc中能够传入函数,并且函数的输入值是整张表,输出为标量、切片、合法列表(元素出现在索引中)、合法索引

# 布尔索引
df['Address'].isin(['street_1','street_2']) # 判断元素是否在某个容器里,返回两列(索引,BOOL)
df.loc[[True if i[-1]=='4' or i[-1]=='7' else False for i in df['Address'].values]]
df.loc[df['Address'].isin(['street_1','street_2'])] # 过滤,只找1,2街的主键的记录
df.loc[df['Gender']=='M'] # 过滤条件后显示表格,找到所有性别为男性的数据

# 小节:本质上说,loc中能传入的只有布尔列表和索引子集构成的列表,只要把握这个原则就很容易理解上面那些操作

2.2.2 iloc 位置索引

通过序号(下标)或者BOOL列表来查找,不用主键。只能用数字bool值,切片右端点不包含

df.iloc[3] # 单行索引
df.iloc[3:5] # 多行索引
df.iloc[:,3] # 单列产品
df.iloc[:,7::-2] # 多列索引
df.iloc[3::4,7::-2] # 混合索引
df.iloc[lambda x:[3]] # 函数式索引

(df['School']=='S_1').values # 返回对应列是否满足条件的bool值
df.iloc[(df['School']=='S_1').values] # 返回满足条件的全部数据
# 小结:iloc中接收的参数只能为整数或整数列表或布尔列表,不能使用布尔Series,如果要用就必须如上把values拿出

2.2.3 [ ]操作符

# Series的[]操作

# 单元素索引
s = pd.Series(df['Math'],index=df.index)
s[1101] # 用的是索引标签
# 多行索引
s[0:4] # 使用的是绝对位置的整数切片,与元素无关,这里容易混淆
# 函数式索引
s[lambda x: x.index[16::-6]] # 注意使用lambda函数时,直接切片(如:s[lambda x: 16::-6])就报错,此时使用的不是绝对位置切片,而是元素切片,非常易错
# 布尔索引
s[s>80]

df[0:2] # 行支持切片取数据

# DataFrame的[]操作

# 单行索引
df[1:2] # 这里非常容易写成df['label'],会报错
# 多行索引
df[3:5] # 用切片,如果是选取指定的某几行,推荐使用loc,否则很可能报错
# 单列索引
df['School']
# 多列索引
df[['School','Math']]
# 函数式索引
df[lambda x:['Math','Physics']]
# 布尔索引
df[df['Gender']=='F']

# 小节:一般来说,[]操作符常用于列选择或布尔选择,尽量避免行的选择

df[(df['Gender']=='F')&(df['Address']=='street_1')] # 过滤数据
df[(df.Physics=='A+')|(df.Math>60)]
df[df['Address'].isin(['street_1','street_4'])&df['Physics'].isin(['A','A+'])]

df.where(df['Gender']=='F').dropna() # 将不满足条件的设为NaN,dropna是删除空值
df.mask(df['Gender']=='M').dropna() # mask 满足条件的,设为NAN,与where相反

2.2.4 布尔索引,数据筛选

# (a)布尔符号:'&','|','~':分别代表和and,或or,取反not
df[(df['Gender']=='F')&(df['Address']=='street_2')]
df[(df['Math']>85)|(df['Address']=='street_7')]
df[~((df['Math']>75)|(df['Address']=='street_1'))]
df[df['grammer'].str.contains("Python|Java")]   # 包含某字符串 

df['A'][df['A']>80]='高' # 大于80的改为高
df.loc[df['A']>100,'A']=10

# loc和[ ]中相应位置都能使用布尔列表选择
df.loc[df['Math']>60,df.columns=='Physics']

# (b) isin方法
df[df['Address'].isin(['street_1','street_4'])&df['Physics'].isin(['A','A+'])]
math_interval = pd.cut(df['Math'],bins=[0,40,60,80,100]) #注意,如果没有类型转换,此时并不是区间类型,而是category类型

df.set_index('Class') # 设定指定列为索引列
df.set_index('Class',append=True) # 利用append参数可以将当前索引维持不变
df.reset_index(drop=True) # 索引重置

2.2.5 常用索引函数

df.where(df['Gender']=='M') # 不满足条件的行全部被设置为NaN
df.where(df['Gender']=='M').dropna() # 通过这种方法筛选结果和[]操作符的结果完全一致
df.where(df['Gender']=='M',np.random.rand(df.shape[0],df.shape[1])).head() # 第一个参数为布尔条件,第二个参数为填充值
df.mask(df['Gender']=='M').dropna() # mask函数与where功能上相反,其余完全一致,即对条件为True的单元进行填充
df.mask(df['Gender']=='M',np.random.rand(df.shape[0],df.shape[1]))

df.query('(Address in ["street_6","street_7"])&(Weight>(70+10))&(ID in [1303,2304,2402])') # query后面接条件字符串
# query函数中的布尔表达式中,下面的符号都是合法的:行列索引名、字符串、and/not/or/&/|/~/not in/in/==/!=、四则运算符,条件存在一个字符串里

2.2.6 重复元素处理,排序

df.duplicated('Class') # 该方法返回了是否重复的布尔列表,默认为两行全部一样叫重复
df.duplicated('Class',keep='last').tail() # 可选参数keep默认为first,即首次出现设为不重复,若为last,则最后一次设为不重复,若为False,则所有重复项为True
df[df.duplicated('country')] # 查看所有重复的数据

df.drop_duplicates('Class')
df.drop_duplicates('Class',keep='last') # 参数与duplicate函数类似
df.drop_duplicates(['School','Class']) # 在传入多列时等价于将多列共同视作一个多级索引,比较重复项
df.sort_values(by=['A','B'],ascending=[False,True],inplace=True,na_position='first') # 按A降序,按B升序,替换掉原来的数据,空值放在前面

2.2.7 抽样函数

df.sample(n=5) # 随机抽取样本
df.sample(frac=0.05) # frac为抽样比
df.sample(n=df.shape[0],replace=True) # replace为是否放回
df.sample(n=35,replace=True).index.is_unique # False
df.sample(n=3,axis=1).head() # axis为抽样维度,默认为0,即抽行
df.sample(n=3,weights=np.random.rand(df.shape[0])).head() # weights为样本权重,自动归一化

#以某一列为权重,这在抽样理论中很常见
df.sample(n=3,weights=df['Math']).head() # 抽到的概率与Math数值成正比
# 可以使用head(),tail()分别查看前n个和后n个值
s3.isnull() # 判断数组中哪些是空值,哪些不是,得到的是一个Bool类型列表
s3.isnull().any() # 使用any函数,查看是否存在至少一个True
s3.isnull().mean() # 查看每列缺失值占比
s = Series(data=[1,2,3,4,5], name="ID") # Series对象本身及其实例都有一个name属性
练习题:
df.query('duration > 60')['shape'].value_counts().index[0] # 超过60s的时间中,哪个形状UFO最多

df['Type 2'].count()/df.shape[0] # 双属性的Pokemon占总体比例的多少,count不会计入NaN

# 在所有种族值(Total)不小于580的Pokemon中,非神兽(Legendary=False)的比例为多少?
df[df['Total']>=580]['Legendary'].value_counts()
df[df['Total']>=580]['Legendary'].value_counts()[1]/df[df['Total']>=580]['Legendary'].count()

# 在第一属性为格斗系(Fighting)的Pokemon中,物攻排名前三高的是哪些?
df[df['Type 1']=='Fighting'].sort_values(by='Attack',ascending=False).iloc[0:3]['Name']

# 请问六项种族指标(HP、物攻、特攻、物防、特防、速度)极差的均值最大的是哪个属性(只考虑第一属性,且均值是对属性而言)?
df[df['Type 1']=='Fighting'].sort_values(by='Attack',ascending=False).iloc[:3]

# 哪个属性(只考虑第一属性)神兽占总Pokemon的比例最高?该属性神兽的种族值均值也是最高的吗?
df['range'] = df.iloc[:,5:11].max(axis=1)-df.iloc[:,5:11].min(axis=1)
attribute = df[['Type 1','range']].set_index('Type 1')
max_range = 0
result = ''
for i in attribute.index.unique():
    temp = attribute.loc[i,:].mean()
    if temp.values[0] > max_range:
        max_range = temp.values[0]
        result = i
result
#NameType 1Type 2TotalHPAttackDefenseSp. AtkSp. DefSpeedGenerationLegendary
01BulbasaurGrassPoison3184549496565451False
12IvysaurGrassPoison4056062638080601False
23VenusaurGrassPoison525808283100100801False
33VenusaurMega VenusaurGrassPoison62580100123122120801False
44CharmanderFireNaN3093952436050651False

2.3 处理丢失数据(缺失值)

np.nan 是一个浮点类型, 可以参与运算,并且跟任何数据进行任何运算得到的结果都是np.nan

空值:在pandas中的空值是""

缺失值:在dataframe中为nan或者naT(缺失时间),在series中为none或者nan即可

s3.isnull().mean() # 查看每列缺失值占比

arr = np.arange(1e5, dtype=object)
%timeit arr.sum() # 2.77 ms ± 144 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

arr = np.arange(1e5, dtype=np.float64)
%timeit arr.sum() # 55.9 µs ± 6.95 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
"""
object类型的运算要比int类型的运算慢得多
计算不同数据类型求和时间
%timeit np.arange(1e5,dtype=xxx).sum()
"""
Series([1,2,3,None]) # pandas中会自动把None优化成np.nan
# pandas中None与np.nan都视作np.nan
  • isnull() 查看是否空值
  • notnull()查看是否不空
  • dropna(): 过滤丢失数据
  • fillna(): 填充丢失数据
df.mean(axis=0) # 计算的是每一列平均值,
df.mean(axis=1) # 计算的是每一行平均值。
df.drop(0,axis=0) # 删除行
df.drop([0,1]) # 删行

del df['column-name'] # 删除列
df.drop('num',axix=1,inplace=True) # 删除列
df.drop(labels=['popularity'], axis=1,inplace = True) # 删除列
df.drop(columns=['A','B'],axis=1) # columns删列专用。labels公用
df.insert(0,'popularity', temp) # 插入一列,temp是Series

# 对dataframe而言,any相当于对其中的每一行或每一列分别执行一次any判断,默认是以列为基准进行判断。默认axis=
df.isnull().any(axis=0) # 使用0值表示沿着每一列或行标签\索引值向下执行方法
res = df.isnull().any(axis=1) # 判断df中哪一行存在空值,使用1值表示沿着每一行或者列标签模向执行对应的方法

k =[]
for i in range(len(data)):
    if type(data.iloc[i,13]) != float:
        k.append(i)
data.drop(labels=k,inplace=True) # 删除满足某种条件的行

函数作用:删除含有空值的行或列

axis:维度,axis=0表示index行,axis=1表示columns列,默认为0

how:"all"表示这一行或列中的元素全部缺失(为nan)才删除这一行或列,"any"表示这一行或列中只要有元素缺失,就删除这一行或列

thresh:保留至少有n个非NaN数据的行/列

subset:在某些列的子集中选择出现了缺失值的列删除,不在子集中的含有缺失值得列或行不会删除(有axis决定是行还是列)

inplace:刷选过缺失值得新数据是存为副本还是直接在原数据上进行修改。

# 空值填充
df.fillna(value=100, inplace=True) # 全部填充
df1.fillna({0:10,1:20,2:30}) # 通过字典填充不同的常数
# df.fillna(value=100, inplace=True)
df2.fillna(method='ffill') # 用前面(默认上面)的值来填充
df.fillna(method="backfill", axis=0, limit=1) # 纵向用后面的值填充,limit限制个数
df['people'].fillna(df.people.value_counts().index[0],inplace=True) # 用众数填充
df['popularity'].fillna(df['popularity'].interpolate()) # 用上下的平均值填充

对于DataFrame来说,还要选择填充的轴axis。记住,对于DataFrame来说:

  • axis=0:index/行
  • axis=1:columns/列

2.4 pandas合并、级联

  • 级联:pd.concat, pd.append
  • 合并:pd.merge, pd.join
函数适用场景调用方法备注
.concat()可用于两个或多个df间行方向(增加行,下同)或列方向(增加列,下同)进行内联或外联拼接操作,默认行拼接,取并集result = pd.concat( [df1,df4], axis=1 )提供了参数axis设置行/列拼接的方向
.merge()可用于两个df间行方向(一般用join代替)或列方向的拼接操作,默认列拼接,取交集(即:存在相同主键的df1和df2的列拼接)result=pd.merge(df1, df2,how=‘left’)提供了类似于SQL数据库连接操作的功能,支持左联、右联、内联和外联等全部四种SQL连接操作类型
.join()可用于df间列方向的拼接操作,默认左列拼接,how=’left’df1.join(df2)支持左联、右联、内联和外联四种操作类型
.append()可用于df间行方向的拼接操作,仅行

2.4.1 pd.append与assign

# append:利用序列添加行(必须指定name)
df_append = df.loc[:3,['Gender','Height']]
s = pd.Series({'Gender':'F','Height':188},name='new_row')
df_append.append(s)

# append:用DataFrame添加表,append函数只是沿着axis=0的方向进行级联
df_temp = pd.DataFrame({'Gender':['F','M'],'Height':[188,176]},index=['new_1','new_2'])
df_append.append(df_temp)

# assign:该方法主要用于添加列,列名直接由参数指定
s = pd.Series(list('abcd'),index=range(4))
df_append.assign(Letter=s)

df_append.assign(col1=lambda x:x['Gender']*2,col2=s) # 可以一次添加多个列

2.4.2 pd.concat级联

  1. 级联方向上的形状不同,同样可以连接
  2. 以索引对齐的方式连接,所以跟顺序没关系
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, copy=True)

objs 需要连接的对象,eg [df1, df2]
axis axis = 0, 表示在水平方向(row)进行连接, axis = 1, 表示在垂直方向(column)进行连接
join outer, 表示index全部需要; inner,表示只取index重合的部分
join_axes 传入需要保留的index
ignore_index 忽略需要连接的frame本身的index。当原本的index没有特别意义的时候可以使用
keys 可以给每个需要连接的df一个labela

score1 = DataFrame(data=np.random.randint(0,100,size=(3,3)), columns=["python","java","php"], index=list("ABC"))
score2 = DataFrame(data=np.random.randint(0,100,size=(4,3)), columns=["java","python","php"], index=list("DBHC"))
display(score1, score2)

pd.concat(objs=(score1, score2)) # # objs 要参与级联的pandas对象的列表、元组,默认纵向

级连会把该方向上索引相同的元素放在一行(一列),index/columns在级联时可以重复

也可以选择忽略ignore_index,重新索引

# 原始表的索引没有实际意义,可以忽略处理,避免重复
order = pd.concat((order1, order2), ignore_index=True)

# 如果级联后,原始索引不能忽略,但又希望避免重复,可以做成多层级表格,key是顶层索引
pd.concat((order1, order2), axis=1, keys=["第一季度","第二季度"])

级联的应用场景:数据表结构相似或相同的汇总操作

不匹配级联

不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致

有3种连接方式:

  • 外连接:补NaN(默认模式)

  • 内连接:只连接匹配的项

  • 连接指定轴 join_axes

df1 = DataFrame(data=np.random.randint(0,100,size=(3,5)), columns=list("ABCDE"))
df2 = DataFrame(data=np.random.randint(-100,0,size=(3,6)), columns=list("BCDEFG"))
display(df1, df2)

pd.concat((df1, df2), join='outer') # outer连接,外连接,保存连接的表的所有字段,缺失值补空值

pd.concat((df1, df2), join='inner') # inner连接, 内连接,只保留公共字段,inner连接会导致数据丢失
# pd.concat((df1, df2), join_axes=[pd.Index(["D","E"])]) # 只保留特定的几个字段,停用

2.4.3 pd.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)
  • 合并只跟列有关,两张表要去找内容相同的列来进行合并

  • 合并的两张表一定存在至少一列,在内容上有对应关系,至少是一对一、一对多、多对多中一种

  • 合并可以同时参考多列进行,这取决于你的业务需求

  • merge与concat的区别在于,merge需要依据某一共同【列】来进行合并

  • 使用pd.merge()合并时,会自动根据两者相同column名称的那一列,作为key来进行合并。

注意每一列元素的顺序不要求一致

  • left: 拼接的左侧DataFrame对象
  • right: 拼接的右侧DataFrame对象
  • on: 要加入的列或索引级别名称。必须在左侧和右侧DataFrame对象中找到。 如果未传递且left_index和right_index为False,则DataFrame中的列的交集将被推断为连接键。
  • left_on:左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
  • right_on: 左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
  • left_index: 如果为True,则使用左侧DataFrame中的索引(行标签)作为其连接键。 对于具有MultiIndex(分层)的DataFrame,级别数必须与右侧DataFrame中的连接键数相匹配。
  • right_index: 与left_index功能相似。
  • how: One of ‘left’, ‘right’, ‘outer’, ‘inner’. 默认inner。inner是取交集,outer取并集。比如left:[‘A’,‘B’,‘C’];right[’'A,‘C’,‘D’];inner取交集的话,left中出现的A会和right中出现的另一个A进行匹配拼接,如果没有是B,在right中没有匹配到,则会丢失。'outer’取并集,出现的A会进行一一匹配,没有同时出现的会将缺失的部分添加缺失值。left=left outer
  • sort: 按字典顺序通过连接键对结果DataFrame进行排序。 默认为True,设置为False将在很多情况下显着提高性能。
  • suffixes: 用于重叠列的字符串后缀元组。 默认为(‘x’,’ y’)。
  • copy: 始终从传递的DataFrame对象复制数据(默认为True),即使不需要重建索引也是如此。
  • indicator:将一列添加到名为_merge的输出DataFrame,其中包含有关每行源的信息。 _merge是分类类型,并且对于其合并键仅出现在“左”DataFrame中的观察值,取得值为left_only,对于其合并键仅出现在“右”DataFrame中的观察值为right_only,并且如果在两者中都找到观察点的合并键,则为left_only。
table1 = pd.read_excel('关系表.xlsx',sheet_name=1,index_col=0)
table2 = pd.read_excel('关系表.xlsx',sheet_name=2,index_col=0)
table3 = pd.read_excel('关系表.xlsx',sheet_name=3,index_col=0)
table4 = pd.read_excel('关系表.xlsx',sheet_name=4,index_col=0)
pd.merge(table1, table2) # 一对一合并
pd.merge(table1, table3) # 多对一合并,table3多

# 1. 默认把字段名字(列标签)相同的列作为合并的依据
# 2. 默认如果有多个列的标签相同,则会同时参考多列合并
pd.merge(table3, table4) # 多对多合并

# 使用on=显式指定哪一列为key,当有多个key相同时使用
pd.merge(table3, table4, on=["手机型号","发货地区"])

# 使用left_on和right_on指定左右两边的列作为key,当左右两边的key都不想等时使用
# 如果合并的列名称不同,会自动保留所有的原始列,可以用户自己决定删除哪一列
r1 = pd.merge(table6, table2, left_on="型号", right_on="手机型号")

# 删除行或者列
r1.drop(labels=["型号"], axis=1, inplace=True) # 删列
r1.drop(labels=[0]) # 删行,0是行索引
del df['categories'] # 删列
df.drop(columns=['categories'], inplace=True) # columns参数直接指定列,labels可指定行或者列

pd.merge(df1, df2, left_on="张三", right_on="张十三").drop("张十三",axis=1) # 删除错误的张十三
内合并与外合并
  • 内合并:只保留两者都有的key(默认模式)
  • 外合并 how=‘outer’:补NaN
  • 左合并、右合并:how=‘left’,how=‘right’
  • inner 内合并,只保留相同的【内容】
  • concat 的内连接: 只保留相同的【标签】
pd.merge(table1, table2, how='inner')
pd.merge(table1, table2, how='outer')
# left\right  根据左表和右表来保留内容,类似左外连接,右外连接
pd.merge(table1, table2, how='left')
pd.merge(table1, table2, how='right')
pd.merge(table3, table4, on="手机型号", suffixes=["_下半年", "_上半年"]) # 当列冲突时,即有多个列名称相同时,需要使用on=来指定哪一个列作为key,配合suffixes指定冲突列名

pd.merge()

  1. 都是以列为合并项
  2. 参与合并的列必须满足一对一、一对多、多对多关系中的至少一种
  3. 参与合并的列的选择应该是选择离散型数据而不是连续型数据
  • how=‘inner’ 合并的方式 内合并 外合并 左合并 右合并
  • on 指定参与合并的列 用于有多列标签相同的情况
  • left_on\right_on 分别制定左右表参与合并的列 用于两张表参与合并的列标签不同的情况
  • suffiexes 一般与on参数一起使用,给相同列标签但是没有参与合并的列添加后缀
  • left_index\right_index 指定索引作为合并的参考值
table1.set_index("手机型号") # 把手机型号列设置为行索引的方法
pd.merge(table7, table2, left_index=True, right_on="手机型号")
pd.merge(table2, table7, left_on="手机型号", right_index=True)

2.5 层次化索引

2.5.1 创建和使用多层索引

# 创建多层索引
tuples = (("上半年","收入"),("上半年","成本"),("上半年","费用"),("下半年","收入"),("下半年","成本"),("下半年","费用"))
pd.MultiIndex.from_tuples(tuples) # 使用元组

arrays = [["上半年","上半年","上半年","下半年","下半年","下半年"],["收入","成本","费用","收入","成本","费用"]]
pd.MultiIndex.from_arrays(arrays) # 使用列表

columns = pd.MultiIndex.from_product([["上半年","下半年"], ["收入","成本","费用"]]) # 最常用最简单
columns # 显示构造pd.MultiIndex
# 使用多层索引
index = pd.MultiIndex.from_product([["期中","期末"],["张三","李四"]])
columns = ["python","java","c"]
data = np.random.randint(0,100,size=(4,3))
df = DataFrame(data=data, index=index, columns=columns) # 行多层

index = ["张三","李四"]
columns = pd.MultiIndex.from_product([["期中","期末"],["python","java","c"]])
data = np.random.randint(0,100,size=(2,6))
DataFrame(data=data, index=index, columns=columns) # 列多层

2.5.2 多层索引对象的索引与切片操作

# Series的操作
python = df["python"]

python.loc[("期中","张三")] # 23, 多层索引使用元祖来表达索引逻辑

a=python.loc["期中"]
a.loc['张三'] # 23, 也可以先把多级索引先变成单级索引,再索引

index1 = ("期中","张三")
index2 = ("期末","李四")
python.loc[index1:index2] # 从期中张三到期末李四的所有python分数
python.loc[[index1, index2]] # 用列表访问了两个值

# DataFrame的操作
# df.loc[index,column]
index = ("期中","张三")
column = "java"
df.loc[index, column] = 100

df[["python","java"]]
df.loc[:,"python":"java"]
df.loc["期中"] # 获取期中的张三和李四的成绩
# 获取期中的李四和期末的张三成绩
index1 = ("期中","李四")
index2 = ("期末","张三")
df.loc[[index1, index2]]
df.loc[index1:index2] # 结果同上

df.iloc[1:] # 隐式索引不受多层级索引的影响,隐式索引永远是单层级

(1) 可以直接使用列名称来进行列索引

行多级索引的索引和切片操作

列多级索引的索引和切片操作

(2) 使用行索引需要用ix(),loc()等函数

【极其重要】推荐使用loc()函数

注意在对行索引的时候,若一级行索引还有多个,对二级行索引会遇到问题!也就是说,无法直接对二级索引进行索引,必须让二级索引变成一级索引后才能对其进行索引,也就是无法直接索引张三

df.loc[("期中","张三"), "c"] = np.nan
df.iloc[0,2] = 101
多层级索引访问的核心
  1. 多层级的索引的表达方式变成元组
  2. 隐式索引的访问方式不受影响(iloc)

2.5.3 多层级索引的变形操作(stack)

# level 具体选择哪层索引,默认-1,内层索引,level=0时,即最外层索引,level相当于列表的下标,0是最外面,-1是最里面

df1 = df.unstack(level=-2) # unstack方法可以将行索引变为列名,行变列
df1.stack(level=-2) # stack方法可以将所有列名,转变为行索引,列变行

2.6 pandas数据处理,重点

- map仅能处理单列
- apply能处理单列或多列,如果直接对df.apply(func,axis=1),那么传入函数的是df,df[‘price’].apply(np.log)传入的是price列
- applymap对全部元素操作,df.applymap(lambda x:“%.2f” % x)
# 分组后直接apply,传入的是每个组的df
arpu =date_buy_3.groupby('date').apply(lambda x:x[x.behavior_type==4].total.sum()/len(x.user_id.unique()))

df.info() # 获取df的摘要,包括Index范围,所有列名,每一列非空值的数量及数据类型
df['len_str'] = df['grammer'].map(lambda x: len(x)) # 求每个字符串的长度

df.describe() # 观察这一系列数据的范围,只对可以运算的列有效。大小、波动趋势等等,便于判断后续对数据采取哪类模型更合适
# 包含计数,平均值,标准差,最小值,最大值,25%,50%,75%分位的数值

df.set_index("A") # 设置A列为索引
df[['ID','age']].astype(float) # 强制类型转换
df['price'] = pd.to_numeric(df['price']) # 将某列转换成数值类型
df['C'] = df['A']+df['C'] # 合并成新列
df["D"] = df["C"].map(str) + df['B'] # 此时C列是数字

df1.sex.value_counts() # sex列分值计数
df1.age.replace('不详',0,inplace=True) # 值替代
df1.age.astype(int) # 类型强制转换

df.people.diff() # 求前后的差值,默认为1(此时第一行会是NaN)
data['收盘价(元)'].pct_change() # 当前元素和之前元素相差的百分比,默认为1
df['people'].rolling(2).mean() # 2个为一组向下求平均值,有交错

# map处理某一列Series,apply处理一个DateFrame
def func(df):
    lst = df['salary'].split('-')
    smin = int(lst[0].strip('k'))
    smax = int(lst[1].strip('k'))
    df['salary'] = int((smin + smax) / 2 * 1000)
    return df
df = df.apply(func,axis=1) # 求平均工资的具体数值
df['sex']=df['sex'].map({'男':1,'女':0})

# map的函数只能接收一个参数
def apply_age(x,bias):
    return x+bias
#以元组的方式传入额外的参数
data["age"] = data["age"].apply(apply_age,args=(-3,))
df['工时']=df['工时']*df['工时']
df['工时']=df['工时']-3
df[['付费额度','计费点id','工时']].apply(np.log)
np.median(df['salary']) # 计算中位数
df.name = df.name.map(lambda x: x + '1班')  # lambda 表达式映射

df1.sort_values(by='comment',ascending=False)[:10] # 按评论数降序排列
df.sort_index(ascending = True) # 按索引升序排序
df2['province']=df2['home'].str.split('·').str[0] # 新建一列,只获取省份

data.groupby('sex')['favour'].sum() # 分男女查看,喜爱数的总数
pd.crosstab(index=data['sex'],columns=data['martial_status']) # 分男女查看,婚姻状况的状态人数,交叉表,用于计数

data.groupby('constellation')['qiushi'].sum() # 分星座查看,qiushi的发布量,agg:聚合
d1=data.groupby(['sex','constellation']).agg({'age':'mean'}) # 分男女查看不同星座下年龄的平均值
heros.role_assist.unique() # 去重后的数值
heros.role_assist.nunique() # 去重后的个数

g1=heros.groupby(['attack_range','role_main']).agg({'hp_max':'mean','hp_growth':'max'}) # 分组后对hp_max求平均值,对hp_growth求最大值

pd.crosstab(index=heros['role_main'],columns=heros['attack_range']) # 交叉表,用于计数

pd.pivot_table(data=heros,index='role_main',values='hp_max',columns='attack_range',aggfunc='count',fill_value=0) # 透视表,行筛选出'role_main',再根据'attack_range'分组统计多少种'hp_max'

pd.pivot_table(data=heros,index='role_main',columns='attack_range',values='hp_max',aggfunc='mean',fill_value=0) # 结果表行是'role_main',列是'attack_range',然后对'hp_max'求平均值
heros.groupby(['role_main','attack_range'])['hp_max'].mean()

df['col1'][~df['col1'].isin(df['col2'])] # 提取第一列中不在第二列出现的数字
df.style.format({'data': '{0:.2%}'.format}) # 格式化data列,23%
df['split'] = df['linestaion'].str.split('_') # 得出的是列表
df[df['industryField'].str.startswith('数据')] # 提取以数据开头的行
df.agg({"salary":np.sum,"score":np.mean}) # agg是聚合函数

快速画图

df.people.plot(kind='pie') # 饼状图
df.people.plot(kind='hist') # 直方图
df.people.plot(kind='bar') # 柱状图
df.people.plot() # 折线图

data[['收盘价(元)','开盘价(元)']].plot()

原始数据最基本的操作一定包括如下三步

  1. 空值的处理
  2. 重复值的处理
  3. 异常值的处理

2.6.1 删除重复元素

使用duplicated()函数检测重复的行,返回元素为布尔类型的Series对象,每个元素对应一行,如果该行不是第一次出现,则元素为True

  • 使用drop_duplicates()函数删除重复的行
  • 使用duplicated()函数查看重复的行
df = DataFrame(data={
    "name":["lucy","tom","jack","tony","mery","rose","black"],
    "python":np.random.randint(0,100,size=(7)),
    "java":np.random.randint(0,100,size=7),
    "php":np.random.randint(0,100,size=7)
})

df.loc[df.duplicated(keep='last')] # 查询重复行,重复值只在行内查找
df.drop_duplicates(keep='last')
df.loc[df.duplicated(subset=["python","java","php"])] # 查询python、java、php成绩相同的行

2.6.2 映射

replace属于fillna的高级版本

映射的含义:创建一个映射关系列表,把values元素和一个特定的标签或者字符串绑定

包含三种操作:

  • replace()函数:替换元素(DataFrame\Series的函数)
  • 最重要:map()函数:新建一列(Series的函数)
  • rename()函数:替换索引(DataFrame的函数)
df.replace(to_replace='tom', value='TOM') # 直接替换字符串
df.replace(to_replace=77, value=100) # 替换数字
df.replace(to_replace=["lucy","tom","jack"], value=["LUCY","MERY","JACK"]) # 使用列表替换
map_dic = {
    "lucy":"LUCY",
    "mery":"MERY",
    "tom":"TOM"
}
df.replace(to_replace=map_dic) # 使用字典替换
df["oldname"] = df.name # 新建一列
df.replace(to_replace={"name":"lucy"}, value="LUCY-1") # 使用字典处理某一列

# 正则替换
# to_replace 使用正则表达式
# regex 必须设置为True
df.replace(to_replace=r't.*', regex=True, value="ContainT") # t开头全部替换
a.replace()函数:替换元素

使用replace()函数,对values进行替换操作

Series替换操作
  • 单值替换
    • 普通替换
    • 字典替换
  • 多值替换
    • 列表替换
    • 字典替换(推荐)

Series参数说明:

  • method:对指定的值使用相邻的值填充,method不能再DataFrame当中使用
  • limit:设定填充次数
DataFrame替换操作
  • 单值替换

    • 普通替换
    • 按列指定单值替换{列标签:替换值}
  • 多值替换

    • 列表替换
    • 单字典替换(推荐)

注意:DataFrame中,无法使用method和limit参数

df.drop("oldname", axis=1, inplace=True)
df.replace(to_replace=[22,77], value=[100,100])
df.replace(to_replace={22:110,77:110}) # 22换成77,77换成110
b.map()函数:新建一列

map 是Series的函数,所以通常被用来对某一列进行整体的映射处理

  • map()可以使用字典映射新一列数据
  • map()中可以使用lambda表达式
  • map()中可以使用方法,可以是自定义的方法

注意

  • map()中不能使用sum之类的函数,for循环
  • map(字典) 字典的键要足以匹配所有的数据,否则出现NaN
map_dic = {'lucy':"北京",'tom':"上海",'jack':"北京", 'tony':"上海", 'mery':"上海", 'rose':"北京"
} # 使用字典map
df["address"] = df.name.map(map_dic)
df['len_str'] = df['grammer'].map(lambda x: len(x))

def map_name(name):
    return map_dic.get(name,name)
df["address"] = df["name"].map(map_name) # 使用函数

# map的函数只能接收一个参数
def apply_age(x,bias):
    return x+bias
#以元组的方式传入额外的参数
data["age"] = data["age"].apply(apply_age,args=(-3,))

# 使用函数map
def score_5(score):
    if score > 90:
        return "A"
    elif score > 80:
        return "B"
    elif score > 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "E"
df.java = df.java.map(score_5)
df["php_5"] = df.php.map(score_5) # 函数映射
df.name = df.name.map(lambda x: x + '1班')  lambda 表达式映射
df.name.transform(lambda x: x + '学员') # transform()和map()类似

# 对一个DataFrame做遍历的时候,默认就是遍历它的列标签
# 完全可以把一个DataFrame当成字典来遍历
for column, v in score.items():
    print(column, v)
    print('---') # 结果是每次打印每列的值

c.rename()函数:替换索引,更改列名

仍然是新建一个字典

使用rename()函数替换行索引

  • mapper 替换所有索引
  • index 替换行索引
  • columns 替换列索引
  • level 指定多维索引的维度
score.rename(columns={"name":"姓名"},inplace=True) # 改列名
score.rename(index={"张三":"tom", "李四":"jack"},inplace=True) # 改索引名
score.set_index("name", inplace=True) # 重新设置name为索引

mapper = {
    "张三":"tom",
    "李四":"jack",
    "语文":"文学",
    "英语":"外语",
    "上学期":"FIRSTCLASS",
    "下学期":"SECONDCLASS"
}
score.rename(mapper=mapper, axis=1) # 用字典替换,axis=1->改列名

total = pd.concat((score1, score2), axis=1, keys=["上学期","下学期"])
pd.concat([score1, score2,score3], axis=0,ignore_index=True) # 可以同时合并多个
total.rename(mapper=mapper, axis=1, level=-2)
score.columns = ["math","chinese","english"] # 也可以这样重命名列名

使用df.std()函数可以求得DataFrame对象每一列的标准差.

尊重业务的需求 5000 10000
异常值通用的界定办法:如果数据是呈标准正态分布的, |data| > 3*|data.std()|
离群点的检测: 数值型的数据都可以使用离群点的方式来检测异常

df = DataFrame(data=np.random.randn(1000,3), columns=list("ABC"))
# 任意一行至少存在一个数的绝对值大于该数所处列的3倍标准差,即认定为满足异常值条件
condition = (np.abs(df) > 3*df.std()).any(axis=1)
df.drop(df.loc[condition].index) # 删除异常列

根据每一列或行的标准差,对DataFrame元素进行过滤。

借助any()或all()函数, 测试是否有True,有一个或以上返回True,反之返回False

对每一列应用筛选条件,去除标准差太大的数据

删除特定索引df.drop(labels,inplace = True)

2.6.3 数据分类/组处理【重点】

grouped_obj = heros.groupby("attack_range") # 根据attack_range分组,产生一个分组对象,这是对象
grouped_obj.groups # 查看分组对象的信息
grouped_obj.mean()[["hp_growth","hp_max"]] # 分组之后一定是聚合, 聚合运算只保留可运算的列

grouped_obj["hp_growth"].mean()
df.groupby('name').agg({'java':'mean','python':'max'}) # 分别对不同的列进行不同的聚合运算

df.groupby('name').mean()['java']
df.groupby('name')['java'].mean() # 效果同上

h1=heros[["name","hp_max","mp_max","attack_range","role_main"]].copy()
avg = DataFrame(h1.groupby("attack_range")["hp_max"].mean())

pd.merge(h1, avg, left_on="attack_range", right_index=True, suffixes=["", "_avg"])

grouped_obj1 = h1.groupby(["attack_range","role_main"])  # 多字段分组
grouped_obj1.groups

grouped_obj1.agg({"hp_max":"mean","mp_max":"mean"}).unstack(level=-2, fill_value=0) # 这其实是一个透视表

数据聚合是数据处理的最后一步,通常是要使每一个数组生成一个单一的数值。

数据分类处理:

  • 分组:先把数据分为几组
  • 用函数处理:为不同组的数据应用不同的函数以转换数据
  • 合并:把不同组得到的结果合并起来

数据分类处理的核心:

  • groupby()函数

  • groups属性查看分组情况

  • 根据item分组,查看结果

总结:数据类型是离散的可以分组,连续的没有意义

  • 获取weight的总和

  • 把总和跟df进行merge合并

  • 使用列表进行多列分组,得到的结果是多层级索引

2.6.4 高级数据聚合apply()

apply直接在groupby后面,传入的是整个df,直接在df后传入的是一列或者一行

使用groupby分组后,也可以使用transform和apply提供自定义函数实现更多的运算

  • df.groupby(‘item’)[‘price’].sum() <==> df.groupby(‘item’)[‘price’].apply(sum)
  • transform和apply都会进行运算,在transform或者apply中传入函数即可
  • transform和apply也可以传入一个lambda表达式
df['new']=df.apply(lambda x:x.php-x.java,axis=1)

h1.groupby("role_main")["mp_max"].mean()
h1.groupby("role_main")["mp_max","mp,min"].apply(np.mean) 
# 如果不是官方聚合函数,可以使用apply传递,按列计算时是每一列当成一个Series传入

# 分组对象的聚合函数,接受的是每一个分组,定制一个最大值和平均值的差的聚合函数
def group_function(x):
    return x.max() - x.mean()
df = DataFrame(h1.groupby("role_main")["mp_max"].apply(group_function))
df.columns = ["max-mean"]

df['yu_shu']=df.apply(lambda x:x['yu']-x['shu'])

注意

  • transform 会自动匹配列索引返回值,不去重
  • apply 会根据分组情况返回值,去重
交叉表

交叉表(cross-tabulation, 简称crosstab)是一种用于计算分组频率的特殊透视表。

pd.crosstab(index=h1["role_main"], columns=h1["attack_range"])
透视表

透视表(pivot table)是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上得分组建将数据分配到各个矩形区域中。在Python和pandas中,可以通过本章所介绍的groupby功能以及(能够利用层次化索引的)DataFrame有一个pivot_table方法,此外还有一个顶级的pandas.pivot_table函数。除了能为groupby提供便利之外,pivot_table还可以添加分项小计(也叫margins)。

# 透视表
pd.pivot_table(data=h1, values="hp_max", index="role_main", columns="attack_range",aggfunc="mean", fill_value=0)

pd.pivot_table(columns='behavior_type',index='hour',data=df,values='user_id',aggfunc=np.size) #透视图
df.groupby(['hour','behavior_type'])['user_id'].count().unstack(1).head() # 效果同上

2.6.5 分箱操作:cut和qcut函数

rfm['M-SCORE']=pd.cut(rfm['M'],bins=[0,50,100,150,200,250,10000],labels=[1,2,3,4,5,6],right=False).astype(float)

df['categories'] = pd.cut(df['salary'],bins = [0,5000,20000,50000],labels=['低','中', '高'])

x:被切分的类数组(array-like)数据,必须是1维的(不能用DataFrame);

bins:bins是被切割后的区间(或者叫“桶”、“箱”、“面元”),有3中形式:一个int型的标量、标量序列(数组)或者pandas.IntervalIndex 。一个int型的标量. 当bins为一个int型的标量时,代表将x平分成bins份。x的范围在每侧扩展0.1%,以包括x的最大值和最小值。标量序列. 标量序列定义了被分割后每一个bin的区间边缘,此时x没有扩 .pandas.IntervalIndex
定义要使用的精确区间。

right:bool型参数,默认为True,表示是否包含区间右部。比如如果bins=[1,2,3],right=True,则区间为(1,2],(2,3];right=False,则区间为(1,2),(2,3)。

labels:给分割后的bins打标签,比如把年龄x分割成年龄段bins后,可以给年龄段打上诸如青年、中年的标签。labels的长度必须和划分后的区间长度相等,比如bins=[1,2,3],划分后有2个区间(1,2],(2,3],则labels的长度必须为2。如果指定labels=False,则返回x中的数据在第几个bin中(从0开始)。

retbins:bool型的参数,表示是否将分割后的bins返回,当bins为一个int型的标量时比较有用,这样可以得到划分后的区间,默认为False。

precision:保留区间小数点的位数,默认为3.

include_lowest:bool型的参数,表示区间的左边是开还是闭的,默认为false,也就是不包含区间左部(闭)duplicates:是否允许重复区间。有两种选择:raise:不允许,drop:允许。

pd.qcut()参数介绍

基于分位数的离散化功能。 将变量离散化为基于等级或样本分位数的相等大小的存储桶。

和pd.cut()相比,pd.qcut()的参数少了两个,少了right和include_lowest两个参数,剩下的参数几乎和pd.cut()一模一样了。

pd.qcut(x, q, labels=None, retbins=False, precision=3, duplicates='raise')

x :一维数组或者Serise

q : 表示分位数的整数或者数组,

  • 如果是分位数的整数,例如10用于十分位,4用于四分位
  • 如果是分位数数组,例如[0,0.25,0.5,0.75,1]用于四分位数

labels : 数组或者布尔值,默认为none,用于指定每个箱体的标签

  • 如果是数组,长度要与分箱个数一致,比如用四分位数分箱,需要指定四个标签
  • 如果为False,则仅返回分箱的整数指示符,即当前数据位于哪个箱子中

rebines :布尔值,可选。 是否显示分箱的分界值。(由于是按照分位数进行分箱,在不知道分位数具体数值的情况下,可以通过这个参数设置显示分界值即分位数的具体数值)

precision:整数,默认3,存储和显示分箱标签的精度。

duplicates:如果分箱临界值不唯一,则引发ValueError或丢弃非唯一

2.6.6 哑变量(独热编码)处理

data = pd.DataFrame({"x":[1,2,3,4,5],"季节":["春","夏","秋","冬","秋"]})
data =pd.get_dummies(data,columns=["季节"],prefix_sep='_',dummy_na=False,drop_first=False)
data
  • data : array-like, Series, or DataFrame 输入的数据
  • prefix : string, get_dummies转换后,列名的前缀,默认为None
  • columns : 指定需要实现类别转换的列名,否则转换所有类别性的列(数字列不会转换)
  • dummy_na : bool, default False 增加一列表示空缺值,如果False就忽略空缺值
  • drop_first : bool, default False 获得k中的k-1个类别值,去除第一个,防止出现多重共线性

注意:

若针对训练数据已经做好了独热编码并建立了模型,而新增的预测数据或分类数据的类别变量未包含完整的类别。此时直接使用pd.get_dummies,会发现与训练集得到的结果不一样。例如:训练数据中季节列中春、夏、秋、冬都出现过,而在新的测试数据中只出现了春与夏,两者的结果会不一样。

为了避免出现这个情况,需要新增以下代码:

data['季节'] = data['季节'].astype(
    'category',
    categories=["春", "夏", "秋", "冬"]
)

2.6.7 时间序列

from datetime import datetime, date, time ,timedelta # datetime模块里的datetime类,date类,time类
x=datetime(2020, 7, 3, 17, 6, 45)  # datetime.datetime(2020, 7, 3, 17, 6, 45),生成一个日期时间对象,也可以只传入日期,时间默认为00:00:00
date(2020, 11, 15)  # datetime.date(2020, 11, 15),创建一个日期
time(18, 23, 45) # datetime.time(10, 53, 21),创建一个时间

x.date()  # 2020-07-03 # 提取日期对象
x.time()  # 17:06:45 # 提取时间对象
x.year  # 2020 # 整数int
x.month  # 7
x.day  # 3
x.hour  # 17
x.minute # 6
x.second # 45

# datetime转字符串,
x.strftime('%m/%d/%Y %H:%M:%S') # '07/03/2020 17:06:45',指定格式
str(x) # '2020-07-03 17:06:45',默认格式
# 字符串转datetime
datetime.strptime('7/6/2011 18:00:00', '%m/%d/%Y %H:%M:%S') #datetime.datetime(2011, 7, 6, 18, 0)
from dateutil.parser import parse # dateutil几乎可以解析所有日期表示形式
parse("Jan 31, 2020 10:45:02 PM") # datetime.datetime(2020, 1, 31, 22, 45, 2)

x.replace(minute=0,second=0) # datetime.datetime(2020, 7, 3, 17, 0)替换
datetime.now() # datetime.datetime(2020, 11, 15, 21, 31, 22, 838494) # 获取当前的日期时间
datetime.now() + timedelta(3)  # 计算三天以后的日期时间

# 时间差可以通过两个datetime对象相减得到,可以创建得到
dt=datetime(2011, 1, 7) - datetime(2008, 6, 24, 8, 15) # datetime.timedelta(days=926, seconds=56700),dt是timedelta对象
c = timedelta(days=9,weeks=2,hours=8,seconds=23,minutes=12) # datetime.timedelta(days=23, seconds=29543),创建一个时间差
c.days # 取时间差的天数,有正负
c.seconds # 取时间差的秒数,为正

"""
在计算机中,时间实际上是用数字表示的。我们把1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为0(1970年以前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp。
"""

x.timestamp()  # 1593767205.0 # 获取对应的时间戳,可用时间戳求相隔小时
datetime.fromtimestamp(t) # 从时间戳获取时间

x.weekday() # 获取a所在的日期是周几,周一是0,以此类推

import pandas as pd
pd.to_datetime(....)
pd.date_range('1/1/2000', periods=3) # 生成连续的3天
pd.date_range('2012-04-01', '2012-06-01')
dfdf['付款日期'].dt # 转换成datetime
(datetime(2019,8,19,18,12,45)-dfdf['付款日期']).dt # 类似timedelta类型

2.6.8 其它功能

# 累加求和
data['sum_Times']=data.groupby(['userID'])['Times'].cumsum()
df = df.explode('标签S') # 标签S是列表类型数据,按列表内的每个元素为一行,炸开,其他列的数据不变
s.diff(periods=-1) # 前一行 - 后一行
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kelly Bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值