数据分析从0到1----Pandas篇(五)

本文涉及的产品
公网NAT网关,每月750个小时 15CU
简介: 数据分析从0到1----Pandas篇

缺失值

缺失值类型

在pandas中,缺失数据显示为NaN。缺失值有3种表示方法,np.nan , None , pd.NA

1、np.nan

缺失值有个特点,它不等于任何值,连自己都不相等。如果用nan和任何其它值比较都会返回nan

print(np.nan == np.nan)
print(type(np.nan))
print(pd.Series([1,2,3]).dtype) # 数据中全为int 那么就是int类型
print(pd.Series([1,np.nan,3]).dtype) # 数据中有一个nan,那么类型变为float

初学者做数据处理遇见object类型会发懵,不知道这是个啥,明明是字符型,导入后就变了,其实是因为缺失值导致的。

除此之外,还要介绍一种针对时间序列的缺失值,它是单独存在的,用NaT表示,是pandas的内置类型,可以视为时间序列版的np.nan,也是与自己不相等

t = pd.Series([pd.Timestamp('20220101')]*3)
print(t)
t[2]=np.nan
print(t)

2、None

还有一种就是None,它要比nan好那么一点,因为它至少自己与自己相等

print(None == None)
print(pd.Series([1,None]))

3、NA标量

pandas1.0以后的版本中引入了一个专门表示缺失值的标量pd.NA,它代表空整数、空布尔值、空字符

对于不同数据类型采取不同的缺失值表示会很乱。pd.NA就是为了统一而存在的。pd.NA的目标是提供一个缺失值指示器,可以在各种数据类型中一致使用(而不是np.nan、None或者NaT分情况使用)。

s = pd.Series([1,2],dtype='Int64')
print(s)
s[1]=pd.NA
print(s)

### pd.NA的一些常用算术运算和比较运算的示例:
# 加法
print('pd.NA+1:\t',pd.NA+1)
# 乘法
print('a*pd.NA:\t','a'*pd.NA)
# 以下两种其中结果为1
print('pd.NA ** 0 :\t',pd.NA**0)
print('1 ** pd.NA:\t',1**pd.NA)
### 比较运算
print('pd.NA == pd.NA:\t',pd.NA==pd.NA)
print('pd.NA < 2.5:\t',pd.NA < 2.5)
print('np.add(pd.NA,1):\t',np.add(pd.NA,1))

缺失值处理

上面讲解完了缺失值的类型,现在讲解一下如何处理缺失值

对于缺失值一般有2种处理方式,要么删除,要么填充(用某个值代替缺失值)。缺失值一般分2种,

  • 一种是某一列的数据缺失。
  • 另一种是整行数据都缺失,即一个空行

首先先来获取缺失值

df = pd.read_excel('16-data/data_test.xlsx')
print(df)
print('---------------------')
print(df.info()) # 查看缺失值的情况
print(df.isnull()) # 缺失值的判断

之后就是对这些缺失值要进行那些处理了,可以进行删除,也可以进行填充

删除缺失值

df. dropna(axis=0, how='any', thresh=None,subset=None,inplace=False)

  • axis:{0或’index’,1或columns"}默认为0确定是否删除了包含缺少值的行或列
    0或“索引”:删除包含缺少值的行。
    1或“列”:删除包含缺少值的列。
  • how:{any’,'all},默认为’any’确定是否从DataFrame中删除行或列,至少一个NA或所有NA.
    “any”:如果存在任何NA值,请删除该行或列。
    “all”:如果所有值都是NA,则删除该行或列。
  • thresh: int需要至少非NA值数据个数。
  • subset:定义在哪些列中查找缺少的值
  • inplace:是否更改源数据
df = pd.DataFrame({'name':['张','三','李','四'],
                  'toy':[np.nan,'dog','cat','mouse'],
                  'born':[pd.NaT,pd.Timestamp('1940-04-25'),pd.NaT,pd.Timestamp('2022-01-02')]})
print(df)
# 删除至少缺少一个元素的行
print(df.dropna())
# 删除至少缺少一个元素的列
print(df.dropna(axis='columns'))
# 删除缺少所有元素的行
print(df.dropna(how='all'))
# 仅保留至少有2个非NA值的行
print(df.dropna(thresh=2))
# 定义在那些列中查找缺少的值
print(df.dropna(subset=['toy']))
# 在同一个变量中保留操作数据
print(df.dropna(inplace=True))
print(df)

如果有缺失值,还可以进行缺失值补充操作

用平均值填充,

用众数填充(大多数时候用这个),众数是指一组数据中出现次数最多的那个数据,一组数据可以有多个众数,也可以没有众数

向前填充(用缺失值的上一行对应字段的值填充,比如D3单元格缺失,那么就用D2单元格的值填充)、

向后填充(与向前填充对应)等方式。

df.fillna(value=None,method=None,axis=None,inplace=False,limit=None,downcast=None)

  • value:用于填充的值(例如0),或者是一个dict/Series/DataFrame值,指定每个索引(对于一个系列)或列(对于一个数据帧)使用哪个值。不在dictSeries/DataFrame中的值将不会被填充。此值不能是列表。
  • method :ffiIl->将上一个有效观察值向前传播 bfill–>将下一个有效观察值向后传播
  • axis :用于填充缺失值的轴。
  • inplace :是否操作源数据
df = pd. DataFrame([
                    [np.nan,2,np.nan,0],
                    [3,4,np.nan,1],
                    [np.nan,np.nan,np.nan,np.nan],
                    [np.nan,3,np.nan,4]
                    ],
                    columns=['A','B','C','D']
                )
print(df)
#将所有NaN元素替换为0
print(df.fillna(0))
# 我们还可以向前或者向后传播非空值
print(df.fillna(method='ffill'))
print(df.fillna(method='bfill'))
# 将列'A','B','C','D'中的所有NaN元素分别替换为0,1,2,3
values = {'A':0,'B':1,'C':2,'D':3}
df.fillna(value=values)
# 只替换第一个NaN元素
df.fillna(0,limit=1)
# 当使用数据填充时,替换会沿着相同的列名和索引进行
df2 = pd.DataFrame(np.random.rand(4,4),columns=['A','B','C','E'])
print(df2)
print('--------------')
print(df)
df.fillna(value=df2)

分组操作

在数据分析中,经常会遇到这样的情况:

根据某一列(或多列)标签把数据划分为不同的组别,然后再对其进行数据分析。

比如,某网站对注册用户的性别或者年龄等进行分组,从而研究出网站用户的画像(特点)。在 Pandas中,要完成数据的分组操作,需要使用groupby()函数,它和 SQL的GROUP BY操作非常相似。

在划分出来的组(group)上应用一些统计函数,从而达到数据分析的目的,比如对分组数据进行聚合、转换,或者过滤。这个过程主要包含以下三步:

  • 拆分(Spliting):表示对数据进行分组;
  • 应用(Applying):对分组数据应用聚合函数,进行相应计算;
  • 合并(Combining):最后汇总计算结果。

在pandas中,实现分组操作的代码很简单,仅需一行代码,在这里,将上面的数据集按照name字段进行划分:

list1 = ['A','B','C']
[list1[x] for x in np.random.randint(0,len(list1),10)]
data = pd.DataFrame({
    'name':[list1[x] for x in np.random.randint(0,len(list1),10)],
    'age':np.random.randint(5,50,10),
    'money':np.random.randint(15,50,10)
})
print(data)
print('---------')
group = data.groupby('name')
print(group)

上述代码会返回一个DataFrameGroupBy对象

那这个生成的DataFrameGroupBy是啥呢?

对data进行了groupby后发生了什么?

python所返回的结果是其内存地址,并不利于直观地理解,为了看看group内部究竟是什么,这里把group转换成list的形式来看一看:

print(list(group))
group.groups

转换成列表的形式后,可以看到,列表由三个元组组成,每个元组中,

第一个元素是组别(这里是按照name进行分组,所以最后分为了A,B,C),

第二个元素的是对应组别下的DataFrame,整个过程可以图解如下:

聚合操作

聚合(Aggregation)操作是groupby后非常常见的操作,会写SQL的朋友对此应该是非常熟悉了。聚合操作可以用来求和、均值、最大值、最小值等

例如

min----求最小

max----求最大

count----计数

mean----求平均

median----求中位数

std----求标准差

var----求方差

print(data.groupby('name').agg('mean')) # 显示声明聚合调用mean方法
print(data.groupby('name').mean()) # 直接调用聚合的mean方法

如果想对针对不同的列求不同的值,比如要计算不同公司员工的平均年龄以及薪水的中位数,可以利用字典进行聚合操作的指定:

data.groupby('name').agg({'age':'max','money':'min'})

转换

transform转换值

transform是一种什么数据操作?

和agg有什么区别呢?

为了更好地理解transform和agg的不同,下面从实际的应用场景出发进行对比。在上面的agg中,我们学会了如何求不同name员工的平均money和age,

如果现在需要在原数据集中新增一列avg_money,代表员工所在的公司的平均薪水(相同公司的员工具有一样的平均薪水),该怎么实现呢?

如果按照正常的步骤来计算,需要先求得不同公司的平均薪水,然后按照员工和公司的对应关系填充到对应的位置,不用transform的话

print(data.groupby('name').mean()['money'])
avg_money_dict = data.groupby('name')['money'].mean().to_dict()
print(avg_money_dict)
avg_money = data[['name','money']].groupby('name')['money'].mean().to_dict()
print(avg_money)
# map()函数可以用于Series对象或DataFrame对象的一列
# 接收函数或字典对象作为参数,返回结果函数或字典映射处理后的值
data['avg_money']=data['name'].map(avg_money)
print(data)
print('---------------------------')
# 如果使用transform的话,只需要一行代码
data['avg_money']=data.groupby('name')['money'].transform('mean')
print(data)

还是以图解的方式来看看进行groupby后transform的实现过程(为了更直观展示,

图中加入了name列,实际按照上面的代码只有money列):

图中的大方框是transform和agg所不一样的地方,对agg而言,会计算得到A,B,C公司对应的均值并直接返回,但对transform而言,则会对每一条数据求得相应的结果,同一组内的样本会有相同的值,组内求完均值后会按照原索引的顺序返回结果,如果有不理解的可以拿这张图和agg那张对比一下。

Apply

它相比agg和transform而言更加灵活,能够传入任意自定义的函数,实现复杂的数据操作对于groupby后的apply,以分组后的子DataFrame作为参数传入指定函数的,基本操作单位是DataFrame

假设我现在需要获取各个公司年龄最大的员工的数据,该怎么实现呢?可以用以下代码实现:

def get_oldest_staff(x):
    # 输入的数据按照age字段进行排序
    df = x.sort_values(by='age',ascending=True)
    # 返回最后一条数据
    return df.iloc[-1]
oldest_staff = data.groupby('name',as_index=False).apply(get_oldest_staff)
print(oldest_staff)

虽然说apply拥有更大的灵活性,但apply的运行效率会比agg和transform更慢。所以,groupby之后能用agg和transform解决的问题还是优先使用这两个方法,实在解决不了了才考虑使用apply进行操作

合并

为了方便维护,一般公司的数据在数据库内都是分表存储的,比如用一个表存储所有用户的基本信息,一个表存储用户的消费情况。所以,在日常的数据处理中,经常需要将两张表拼接起来使用,这样的操作对应到SQL中是join,在Pandas中则是用merge来实现。这就讲一下merge的主要原理。

上面的引入部分说到merge是用来拼接两张表的,那么拼接时自然就需要将用户信息一 一对应地进行拼接,所以进行拼接的两张表需要有一个共同的识别用户的键(key)。总结来说,整个merge的过程就是将信息一 一对应匹配的过程,下面介绍merge的四种类型,分别为inner,left , right和outer 。

pd.merge(left,right,how: str = 'inner',on=None,left_on=None,right_on=None,left_index: bool =False,right_index: bool = False,sort: bool =False,suffixes=('_x', 'Y'),copy: bool = True,

indicator: bool = False,validate=None,)

先来看一下数据对应的情况,分为一一对应和一对多两种情况

# 一条数据对应一条数据
df1 = pd.DataFrame({
    'userid':['a','b','c','d'],
    'age':[12,13,14,15]
})
df2 = pd.DataFrame({
    'userid':['a','c'],
    'money':[123,543]
})
print(pd.merge(df1,df2,on='userid')) # 第一种合并方法
print('------------------------')
print(df1.merge(df2,on='userid')) # 第二种方法合并

# 一条数据对应多条数据
df1 = pd.DataFrame({
    'userid':['a','b','c','d'],
    'age':[12,13,14,15]
})
df2 = pd.DataFrame({
    'userid':['a','c','a','b'],
    'money':[123,543,546,123]
})
print(df1.merge(df2,on='userid'))
print('------------')
print(pd.merge(df1,df2,on='userid'))

'left和’right的merge方式其实是类似的,分别被称为左连接和右连接。这两种方法是可以互相转换的,所以在这里放在一起介绍。

‘left’

merge时,以左边表格的键为基准进行配对,如果左边表格中的键在右边不存在,则用缺失值NaN填充。

‘right’

merge时,以右边表格的键为基准进行配对,如果右边表格中的键在左边不存在,则用缺失值NaN填充。

简而言之也就是:左连接,则左表的所有数据都必须显示,右表只是填充对应的数据而已,

右连接则与左连接相反.

什么意思呢?用一个例子来具体解释一下,这是演示的数据

# 左连接
df1 = pd.DataFrame({
    'userid':['a','b','c','d'],
    'age':[12,13,14,15]
})
df2 = pd.DataFrame({
    'userid':['a','c','e'],
    'money':[123,543,546]
})
print(df1.merge(df2,on='userid',how='left'))
print('------------')
print(pd.merge(df1,df2,on='userid',how='left')) # 左连接

# 右连接
df1 = pd.DataFrame({
    'userid':['a','b','c','d'],
    'age':[12,13,14,15]
})
df2 = pd.DataFrame({
    'userid':['a','c','e'],
    'money':[123,543,546]
})
print(df1.merge(df2,on='userid',how='right'))
print('------------')
print(pd.merge(df1,df2,on='userid',how='right')) # 右连接

与此同时还有一个outer外连接,他将展示量表所有的信息,没有对应的则为NaN

# 外连接
df1 = pd.DataFrame({
    'userid':['a','b','c','d'],
    'age':[12,13,14,15]
})
df2 = pd.DataFrame({
    'userid':['a','c','e'],
    'money':[123,543,546]
})
print(df1.merge(df2,on='userid',how='outer'))
print('------------')
print(pd.merge(df1,df2,on='userid',how='outer')) # 外连接

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
13天前
|
数据采集 存储 数据挖掘
Python数据分析:Pandas库的高效数据处理技巧
【10月更文挑战第27天】在数据分析领域,Python的Pandas库因其强大的数据处理能力而备受青睐。本文介绍了Pandas在数据导入、清洗、转换、聚合、时间序列分析和数据合并等方面的高效技巧,帮助数据分析师快速处理复杂数据集,提高工作效率。
40 0
|
3月前
|
数据采集 数据可视化 数据挖掘
数据分析大神养成记:Python+Pandas+Matplotlib助你飞跃!
在数字化时代,数据分析至关重要,而Python凭借其强大的数据处理能力和丰富的库支持,已成为该领域的首选工具。Python作为基石,提供简洁语法和全面功能,适用于从数据预处理到高级分析的各种任务。Pandas库则像是神兵利器,其DataFrame结构让表格型数据的处理变得简单高效,支持数据的增删改查及复杂变换。配合Matplotlib这一数据可视化的魔法棒,能以直观图表展现数据分析结果。掌握这三大神器,你也能成为数据分析领域的高手!
77 2
|
7天前
|
机器学习/深度学习 数据采集 数据挖掘
解锁 Python 数据分析新境界:Pandas 与 NumPy 高级技巧深度剖析
Pandas 和 NumPy 是 Python 中不可或缺的数据处理和分析工具。本文通过实际案例深入剖析了 Pandas 的数据清洗、NumPy 的数组运算、结合两者进行数据分析和特征工程,以及 Pandas 的时间序列处理功能。这些高级技巧能够帮助我们更高效、准确地处理和分析数据,为决策提供支持。
19 2
|
14天前
|
存储 数据挖掘 数据处理
Python数据分析:Pandas库的高效数据处理技巧
【10月更文挑战第26天】Python 是数据分析领域的热门语言,Pandas 库以其高效的数据处理功能成为数据科学家的利器。本文介绍 Pandas 在数据读取、筛选、分组、转换和合并等方面的高效技巧,并通过示例代码展示其实际应用。
29 2
|
5天前
|
并行计算 数据挖掘 大数据
Python数据分析实战:利用Pandas处理大数据集
Python数据分析实战:利用Pandas处理大数据集
|
5天前
|
数据采集 数据可视化 数据挖掘
利用Python进行数据分析:Pandas库实战指南
利用Python进行数据分析:Pandas库实战指南
|
1月前
|
机器学习/深度学习 数据采集 算法
探索Python科学计算的边界:NumPy、Pandas与SciPy在大规模数据分析中的高级应用
【10月更文挑战第5天】随着数据科学和机器学习领域的快速发展,处理大规模数据集的能力变得至关重要。Python凭借其强大的生态系统,尤其是NumPy、Pandas和SciPy等库的支持,在这个领域占据了重要地位。本文将深入探讨这些库如何帮助科学家和工程师高效地进行数据分析,并通过实际案例来展示它们的一些高级应用。
47 0
探索Python科学计算的边界:NumPy、Pandas与SciPy在大规模数据分析中的高级应用
|
1月前
|
数据采集 数据挖掘 API
Python数据分析加速器:深度挖掘Pandas与NumPy的高级功能
在Python数据分析的世界里,Pandas和NumPy无疑是两颗璀璨的明星,它们为数据科学家和工程师提供了强大而灵活的工具集,用于处理、分析和探索数据。今天,我们将一起深入探索这两个库的高级功能,看看它们如何成为数据分析的加速器。
40 1
|
1月前
|
数据采集 数据可视化 数据挖掘
Python 数据分析实战:使用 Pandas 进行数据清洗与可视化
【10月更文挑战第3天】Python 数据分析实战:使用 Pandas 进行数据清洗与可视化
83 0
|
2月前
|
数据采集 数据可视化 数据挖掘
数据分析大神养成记:Python+Pandas+Matplotlib助你飞跃!
【9月更文挑战第2天】数据分析大神养成记:Python+Pandas+Matplotlib助你飞跃!
56 5