2020-08-02--Pandas-01--常用数据结构
Pandas概述
Pandas(Python Data Analysis Library )是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。Pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之一。
Pandas是Python的一个数据分析包,最初由AQR Capital Management于2008年4月开发,并于2009年底开源出来,目前由专注于Python数据包开发的PyData开发team继续开发和维护,属于PyData项目的一部分。Pandas最初被作为金融数据分析工具而开发出来。
Pandas含有使数据分析工作变得更快更简单的高级数据结构和操作工具。pandas是基于Numpy构建的,让以Numpy为中心的应用变得更简单。
Pandas专用于数据预处理和数据分析的Python第三方库,最适合处理大型结构化表格数据
- Pandas是2008年Wes McKinney于AQR资本做量化分析师时创建
- Pandas借鉴了R的数据结构
- Pandas基于Numpy搭建,支持Numpy中定义的大部分计算
- Pandas含有使数据分析工作更简单高效的高级数据结构和操作工具
- Pandas底层用Cython和C做了速度优化,极大提高了执行效率
Pandas 有很多高级的功能,但是想要掌握高级功能前,需要先掌握它的基础知识,Pandas 中的数据结构算是非常基础的知识之一了。
Pandas 常用的数据结构有两种:Series 和 DataFrame。这些数据结构构建在 Numpy 数组之上,这意味着它们效率很高。我们来分别看看这些数据结构都长什么样子吧。
# 导入相关库
import numpy as np
import pandas as pd
Series
1.简介,创建Series基本结构
Series 是一个带有 名称 和索引的一维数组,既然是数组,肯定要说到的就是数组中的元素类型,在 Series 中包含的数据类型可以是整数、浮点、字符串、Python对象等。
假定有一个场景是:存储一些用户的信息,暂时只包括年龄信息。
我们可以通过 Series 来存储,这里我们通过 Series 存储了四个年龄:18/30/25/40,只需将要存储的数据构建成一个数组,然后赋值给data参数即可。
import pandas as pd
user_age = pd.Series(data=[18,30,25,40])
print(type(user_age)) # <class 'pandas.core.series.Series'>
print(user_age)
# 0 18
# 1 30
# 2 25
# 3 40
# dtype: int64
# 支持多种基本数据类型
user_age = pd.Series(data=[18,30,'25岁',40])
print(type(user_age)) # <class 'pandas.core.series.Series'>
print(user_age)
# 0 18
# 1 30
# 2 25岁
# 3 40
# dtype: object
并且可以看出,Series中的数据支持多种数据类型,它会自动识别其中数据的数据类型。
可以看到,已经正确将多个年龄存储到 Series 中了,你可能会想,单独存储了年龄有什么用,我怎么知道这个年龄属于哪个用户呢?
我们可以通过 Series 的 index(索引)来解决这个问题。由于有四个年龄,自然地也需要四个姓名,所以我们需要构建一个与 data 长度相同的数组,然后通过下面的操作即可满足要求。
# 设置索引
user_age.index = ['zhangsan','lisi','wangwu','zhaoliu']
print(user_age)
# zhangsan 18
# lisi 30
# wangwu 25岁
# zhaoliu 40
# dtype: object
你看,现在姓名与年龄已经完全对应上了。虽然说我们自己知道 Tom/Bob 这些是姓名,但是别人不知道啊,我们怎么告诉他人呢?
要想让别人知道,我们可以为 index 起个总名字,类似于表格的样子。
# 为索引设置name
user_age.index.name = 'name'
print(user_age)
# name
# zhangsan 18
# lisi 30
# wangwu 25岁
# zhaoliu 40
# dtype: object
可能你还会想,如果别人在看我写的代码,怎么能快速的知道我这写的到底是什么玩意呢?
别急,就像我们给index起名字一样,我们也可以给 整个Series 起个总名字。
# 为Series设置name
user_age.name = 'user_age_info'
print(user_age)
# name
# zhangsan 18
# lisi 30
# wangwu 25岁
# zhaoliu 40
# Name: user_age_info, dtype: object
通过上面一系列的操作,我们对 Series 的结构上有了基本的了解,
简单来说,一个 Series 包括了:
- data :对应Series对象的数据。
- index:Series对象数据的索引,在索引上也有name属性, 默认索引为RangeIndex(start=0, stop=4, step=1):0,1,2,3。name属性不设置时,默认返回None。
- name:整个Series对象的名称。没设置时,返回None。
上面的操作非常方便做演示来使用,如果想要快速实现上面的功能,可以通过以下方式来实现。
2.快速创建Series基本结构
创建索引信息:数据和name:
- 在pd.Index(data=[],name=)
创建Series对象:data,索引index,name属性,dtype数据类型(不写自动推断) - 在pd.Series(data=[],index=,name=,dtype=)
# 快速创建Series基本结构
name = pd.Index(['zhangsan','lisi','wangwu','zhaoliu'],name='姓名')
age = [18,30,'25岁',40]
user = pd.Series(data=age,index=name,name='user_age')
print(user)
# 姓名
# zhangsan 18
# lisi 30
# wangwu 25岁
# zhaoliu 40
# Name: user_age, dtype: object
需要说明的是我们在构造 Series 的时候,并没有设定每个元素的数据类型,这个时候,Pandas 会自动判断一个数据类型,并作为 Series 的类型。
当然了,我们也可以自己手动指定数据类型。
user_age = pd.Series(data=[18, 30, 25, 40], index=name, name="user_age_info", dtype=float)
print(user_age)
# 姓名
# zhangsan 18.0
# lisi 30.0
# wangwu 25.0
# zhaoliu 40.0
# Name: user_age_info, dtype: float64
3.Series 像什么(数据获取)
Series 包含了 dict 的特点,也就意味着可以使用与 dict 类似的一些操作。我们可以将 index 中的元素看成是 dict 中的 key。
两种方式获取数据,与dict中获取数据一样:
# 获取数据,注意user时Series对象,而不是Series对象的name属性值
age =user['zhangsan']
print(age,type(age))
# 18 <class 'int'>
age = user.get('lisi')
print(age,type(age))
# 30 <class 'int'>
Series 除了像 dict 外,也非常像 ndarray,这也就意味着可以采用切片操作。
- 下标获取单个元素-------user[1]
- 下标获取多个元素-------[[1,3]]
- 切片获取-----------user[:3]
- 花式索引----------user[表达式(返回bool值数组)]
# 通过数组下标的方式获取数据
age = user[0]
print(age)
# 18
# 切片操作,返回Series对象
age = user[:3]
print(age)
# 姓名
# zhangsan 18
# lisi 30
# wangwu 25岁
# Name: user_age, dtype: object
# 花式索引--索引为表达式:该表达式返回一个都为bool值的数组
# 返回Series对象
age = user[user>20]
print(age)
# 姓名
# lisi 30.0
# wangwu 25.0
# zhaoliu 40.0
# Name: user_age_info, dtype: float64
# 获取多个元素--第二个元素和最后一个元素
age = user[[1,3]]
print(age)
# 姓名
# lisi 30.0
# zhaoliu 40.0
# Name: user_age_info, dtype: float64
总结:
也就是说:Series对象获取元素可以像字典一样通过key获取,也可以像pythonlist和ndarray一样通过下标,切片,花式索引获取数据。
当然也可以获取到元素后,就可以操作元素了,例如改变元素的值。
4.Series 的向量化操作
Series 与 ndarray 一样,也是支持向量化操作的。同时也可以传递给大多数期望 ndarray 的 NumPy 方法。
# 向量操作
print(user)
# 姓名
# zhangsan 18.0
# lisi 30.0
# wangwu 25.0
# zhaoliu 40.0
# Name: user_age_info, dtype: float64
# 数据整体加一
user_add = user+1
print(user_add)
# 姓名
# zhangsan 19.0
# lisi 31.0
# wangwu 26.0
# zhaoliu 41.0
# Name: user_age_info, dtype: float64
exp = np.exp(user)
print(exp)
# 姓名
# zhangsan 6.565997e+07
# lisi 1.068647e+13
# wangwu 7.200490e+10
# zhaoliu 2.353853e+17
# Name: user_age_info, dtype: float64
DataFrame
1.DataFrame的创建
DataFrame 是一个带有索引的二维数据结构,每列可以有自己的名字,并且可以有不同的数据类型。你可以把它想象成一个 excel 表格或者数据库中的一张表,DataFrame 是最常用的 Pandas 对象。
我们继续使用之前的实例来讲解 DataFrame,在存储用户信息时,除了年龄之外,我还想存储用户所在的城市。如何通过 DataFrame 实现呢?
可以构建一个 dict,key 是需要存储的信息,value 是信息列表。然后将 dict 传递给 data 参数。
import pandas as pd
# 创建索引
index = pd.Index(data=["Tom", "Bob", "Mary", "James"], name="name")
# 数据
data = {
"age": [18, 30, 25, 40],
"city": ["BeiJing", "ShangHai", "GuangZhou", "ShenZhen"]
}
# 创建DataFrame对象
user_info = pd.DataFrame(data=data, index=index)
print(user_info)
# age city
# name
# Tom 18 BeiJing
# Bob 30 ShangHai
# Mary 25 GuangZhou
# James 40 ShenZhen
print(type(user_info))
# <class 'pandas.core.frame.DataFrame'>
可以看到,我们成功构建了一个 DataFrame,这个 DataFrame 的索引是用户姓名,还有两列分别是用户的年龄和城市信息。
除了上面这种传入 dict 的方式构建外,我们还可以通过另外一种方式来构建。这种方式是先构建一个二维数组,然后再生成一个列名称列表。
# 创建索引
index = pd.Index(data=["Tom", "Bob", "Mary", "James"], name="name")
data = [[18, "BeiJing"],
[30, "ShangHai"],
[25, "GuangZhou"],
[40, "ShenZhen"]]
columns = ["age", "city"]
user_info = pd.DataFrame(data=data, index=index, columns=columns)
print(user_info)
# age city
# name
# Tom 18 BeiJing
# Bob 30 ShangHai
# Mary 25 GuangZhou
# James 40 ShenZhen
总结:pd.DataFrame(data=数据,index=行的表识(其中有name属性),columns=列标识)
pd.Index(data=[索引数据项],name=数据名)
2.访问行
在生成了 DataFrame 之后,可以看到,每一行就表示某一个用户的信息,假如我想要访问 Tom 的信息,我该如何操作呢?
一种办法是通过索引名来访问某行,这种办法需要借助 loc 方法。
user.loc[index]
# 访问行
tom = user_info.loc['Tom']
print(tom)
# age 18
# city BeiJing
# Name: Tom, dtype: object
print(type(tom))
# <class 'pandas.core.series.Series'>
获取多个元素可以使用索引切片的方式获取:
# 访问行
tom = user_info.loc['Tom':]
print(tom)
# age city
# name
# Tom 18 BeiJing
# Bob 30 ShangHai
# Mary 25 GuangZhou
# James 40 ShenZhen
print(type(tom))
# <class 'pandas.core.frame.DataFrame'>
除了直接通过索引名来访问某一行数据之外,还可以通过这行所在的位置来选择这一行。
user.iloc[0]
tom = user_info.iloc[0]
print(tom)
# age 18
# city BeiJing
# Name: Tom, dtype: object
print(type(tom))
# <class 'pandas.core.series.Series'>
借助行切片可以轻松完成,来看这里。
切片访问:
# 切片访问
user = user_info.iloc[:3]
print(user)
# age city
# name
# Tom 18 BeiJing
# Bob 30 ShangHai
# Mary 25 GuangZhou
print(type(user))
# <class 'pandas.core.frame.DataFrame'>
当访问一条数据信息时,返回的类型为Series类型,多条时返回DataFrame类型。
2.访问列
学会了如何访问行数据之外,自然而然会想到如何访问列。我们可以通过属性(“.列名”)的方式来访问该列的数据,也可以通过[column]的形式来访问该列的数据。
假如我想获取所有用户的年龄,那么可以这样操作。
user.age
user['age']
# 访问列
print(user.age)
print(user['age'])
# name
# Tom 18
# Bob 30
# Mary 25
# Name: age, dtype: int64
print(type(user.age)) # <class 'pandas.core.series.Series'>
如果想要同时获取年龄和城市该如何操作呢?
# 多值
print(user[['age','city']])
# age city
# name
# Tom 18 BeiJing
# Bob 30 ShangHai
# Mary 25 GuangZhou
print(type(user[['age','city']])) # # <class 'pandas.core.frame.DataFrame'>
3.新增/删除行
在生成了 DataFrame 之后,突然你发现好像缺失了用户的性别这个信息,那么如何添加呢?
如果所有的性别都一样,我们可以通过传入一个标量,Pandas 会自动帮我们广播来填充所有的位置。
# 新增列
user['sex'] = '男'
print(user)
# age city sex
# name
# Tom 18 BeiJing 男
# Bob 30 ShangHai 男
# Mary 25 GuangZhou 男
如果想要删除某一列,可以使用 pop 方法来完成。
# 删除列
user.pop('sex')
print(user)
# age city
# name
# Tom 18 BeiJing
# Bob 30 ShangHai
# Mary 25 GuangZhou
如果用户的性别不一致的时候,并且数据量不是很大时,我们可以通过传入一个 list 来添加新的一列:
# 设置不同的sex
user['sex'] = ['男','女','女']
print(user)
# age city sex
# name
# Tom 18 BeiJing 男
# Bob 30 ShangHai 女
# Mary 25 GuangZhou 女
通过上面的例子可以看出,我们创建新的列的时候都是在原有的 DataFrame 上修改的,也就是说如果添加了新的一列之后,原有的 DataFrame 会发生改变。
如果想要保证原有的 DataFrame 不改变的话,我们可以通过 assign 方法来创建新的一列。返回一个新的DataFrame对象。
# 创建新的一列,该列的值为每一行中的age+1
user2 = user.assign(age_add_one=user['age']+1)
print(user2)
# age city sex age_add_one
# name
# Tom 18 BeiJing 男 19
# Bob 30 ShangHai 女 31
# Mary 25 GuangZhou 女 26
还有一种就是根据已有列的值不同,来创建新的列的值。
print(user)
# age city sex
# name
# Tom 18 BeiJing 男
# Bob 30 ShangHai 女
# Mary 25 GuangZhou 女
# 新建一列avail,该列的值由每列的age决定,若age大于18,则该列的值为有效,否则为无效。
user3 = user.assign(avail=np.where(user['age']>18,'有效','无效'))
print(user3)
# age city sex avail
# name
# Tom 18 BeiJing 男 无效
# Bob 30 ShangHai 女 有效
# Mary 25 GuangZhou 女 有效