DataFrame常用操作
前言
DataFrame是最常用的pandas对象。完成数据读取后,数据就以DataFrame数据结构存储在内存中,但此时并不能直接开始统计分析工作,需要使用DataFrame的属性与方法对数据的分布、大小等基本的数据状况有一个了解。只有对数据基本状况有了一个深度的了解,才能够依据数据的状况,进行量身定制的统计分析。
1 查看DataFrame的常用属性
DataFrame基础属性有values、index、columns和dtypes,分别可以获取元素、索引、列名和类型。
例如,订单性详情表的基本属性如下:
from sqlalchemy import create_engine
import pandas as pd
## 创建数据库连接
## 创建一个mysql连接器,用户名为root,密码为1234
username = 'root' # 替换为你的数据库用户名
password = '1234' # 替换为你的数据库密码
host = 'localhost' # 数据库地址
database = 'testdb' # 数据库名称
# 创建数据库连接
engine = create_engine(f'mysql+pymysql://{username}:{password}@{host}/{database}')
detail= pd.read_sql_table('meal_order_detail1',con = engine)
print('订单详情表的索引为:', detail.index)
print('订单详情表的所有值为:','\n', detail.values)
print('订单详情表的列名为:','\n', detail.columns)
print('订单详情表的数据类型为:','\n', detail.dtypes)
输出:
除上述4个基本属性,size、nidm和shape这几个属性能够获取DataFrame的元素个数、维度数和数据形状(行列数目),如下所示:
## 查看DataFrame的元素个数
print('订单详情表的元素个数为:', detail.size)
print('订单详情表的维度数为:', detail.ndim) ## 查看DataFrame的维度数
print('订单详情表的形状为:', detail.shape) ## 查看DataFrame的形状
输出:
另外,T属性能够实现DataFrame的转置(行列转换)。在某些特殊场景下,某些函数方法只能作用于列或者行,此时就可以试着用转置解决问题,如下:
print('订单详情表转置前形状为:',detail.shape)
print('订单详情表转置后形状为为:',detail.T.shape)
输出:
2 增删改查DataFrame数据
DataFrame作为一种二维数据表结构,能够像数据库一样实现增删改查操作,如添加一行,删除一行,添加一列,删除一列等。
2.1 查DataFrame数据
2.1.1 基本查看方式
DataFrame的单列数据为一个Series,根据DataFrame的定义,DataFrame是一个带有标签的二维数组,每个标签相当于每一列的列名。
使用字典访问内部数据的方式访问DataFrame单列数据
## 使用访问字典方式取出orderInfo中的某一列
order_id = detail['order_id']
print('订单详情表中的order_id的形状为:','\n',order_id.shape)
输出:
使用访问属性的方式访问DataFrame单列数据
## 使用访问属性方式取出orderInfo中的菜品名称列
dishes_name = detail.dishes_name
print('订单详情表中的dishes_name的形状为:',dishes_name.shape)
输出:
以上两种方法均可以获得DataFrame中的某一列数据,但是使用属性的方法访问数据并不建议使用。因为多数时候数据的列名为英文,以属性方式访问某一列的形式和DataFrame属性访问,其方法和使用的格式相同,难免存在部分列名和pandas提供的方法相同,这时候会引起程序混乱。
访问DataFrame中某一列的某几行时,单独一列的DataFrame可以视为一个Series,而访问一个Series和访问一个一维的ndarray基本相同。
DataFrame单列多行数据获取
dishes_name5 = detail['dishes_name'][:5]
print('订单详情表中的dishes_name前5个元素为:','\n',dishes_name5)
输出:
访问DataFrame多列的多行数据
orderDish = detail[['order_id','dishes_name']][:5]
print('订单详情表中的order_id和dishes_name前5个元素为:', '\n',orderDish)
如果只需要访问DataFram某几行数据,则实现方式和上述的访问多列多行相似,选择所有列,使用“:”代替即可
order5 = detail[:][1:6]
print('订单详情表的1-6行元素为:','\n',order5)
输出:
通过DataFrame提供的方法head和tail也可以得到多行数据,但是用这两种方法得到的数据都是从开始或者末尾获取的连续数据。
如下:
print('订单详情表中前五行数据为','\n',detail.head())
print('订单详情表中后五个元素为:','\n',detail.tail())
输出:
上述代码中的head和tail方法使用的都是默认参数,所以访问的是前。后5行,只要在方法后的()中输入访问行数,即可实现目标行数的查看。
2.1.2 DataFrame的loc、iloc访问方式
pandas提供了loc和iloc访问方式,更加灵活的实现数据访问。
- loc方法:是针对DataFrame索引名称的切片方法,如果传入的不是索引名称,那么切片操作将无法执行。利用该方法可以实现所有单层索引切片操作。loc方法的使用方法如下:
DataFrame.loc[行索引名称或条件,列索引名称] - iloc方法:iloc接受的必须是行索引和列索引的位置,方法如下:
DataFrame.iloc[行索引名位置,列索引位置]
使用loc和iloc实现单列切片
dishes_name1 = detail.loc[:,'dishes_name']
print('使用loc提取dishes_name列的size为:', dishes_name1.size)
dishes_name2 = detail.iloc[:,3]
print('使用iloc提取第3列的size为:', dishes_name2.size)
输出:
使用loc方法和iloc实现多列切片,其原理就是将多列的列名或者位置作为一个列表或者数据传入,如下:
orderDish1 = detail.loc[:,['order_id','dishes_name']]
print('使用loc提取order_id和dishes_name列的size为:', orderDish1.size)
orderDish2 = detail.iloc[:,[1,3]]
print('使用iloc提取第1和第3列的size为:', orderDish2.size)
输出:
使用loc。iloc方法可以取出DataFrame中的任意数据,如下:
print('列名为order_id和dishes_name的行名为3的数据为:\n',
detail.loc[3,['order_id','dishes_name']])
print('列名为order_id和dishes_name行名为2,3,4,5,6的数据为:\n',
detail.loc[2:6,['order_id','dishes_name']])
print('列位置为1和3行位置为3的数据为:\n',detail.iloc[3,[1,3]])
print('列位置为1和3行位置为2,3,4,5,6的数据为:\n',
detail.iloc[2:7,[1,3]])
输出:
在使用loc方法时,如果内部传入的行索引名称为一个区间,则前后均为闭区间;使用iloc方法时,如果内部传入的行索引位置或列索引位置为区间,则为前闭后开区间。
loc内部还可以传入表达式,结果会返回满足表达式的所有值。
## loc内部传入表达式
print('detail中order_id为458的dishes_name为:\n',
detail.loc[detail['order_id']=='458',
['order_id','dishes_name']])
输出:
print('detail中order_id为458的第1,5列数据为:\n',
detail.iloc[detail['order_id']=='458',[1,5]])
输出:
上述出错,是因为此处条件返回的为一个布尔值Series,而iloc可以接收的数据烈性并不包括Series。根据Series的构成,只要取出Series的values即可。
print('detail中order_id为458的第1,5列数据为:\n',
detail.iloc[(detail['order_id']=='458').values,[1,5]])
输出:
总的来说,loc更加灵活多变,可读性更高;iloc代码简洁,可读性不高。
2.2 改DataFrame数据
更改DataFrame中的数据的原理是将这部分数据提取出来,重新赋值为新的数据,如下:
##将order_id为458的,变换为45800
detail.loc[detail['order_id']=='458','order_id'] = '45800'
print('更改后detail中order_id为458的order_id为:\n',
detail.loc[detail['order_id']=='458','order_id'])
print('更改后detail中order_id为45800的order_id为:\n',
detail.loc[detail['order_id']=='45800','order_id'])
输出:
注意:数据更改时直接对DataFrame原数据更改,操作无法撤销。如果做出更改,则需要对更改条件进行确认或对数据备份。
2.3 增添数据
添加一列只需要新建一个列索引,并对该索引下的数据进行赋值操作即可。
为DataFrame新增一列非定值
detail['payment'] = detail['counts']*detail['amounts']
print('detail新增列payment的前五行为:','\n',
detail['payment'].head())
输出:
如果新增一列值是相同的,则直接赋值一个常量即可。例如:
detail['pay_way'] = '现金支付'
print('detail新增列pay_way的前五行为:','\n',
detail['pay_way'].head())
输出:
2.4 删除数据
删除某列或某行数据是drop方法,用法如下:
DataFrames.drop(labels,axix=0,level=None,inplace=False,errors='raise')
参数说明:
参数 | 说明 |
---|---|
labels | 接收string或array,代表删除的行或列的标签。无默认 |
axix | 接收0或1,操作的轴向,默认为0 |
level | 接收int或者索引名,代表标签所在级别,默认为None |
inplace | 接收boolean,代表操作是否对原数据生效,默认为False |
例如:
print('删除pay_way前deatil的列索引为:','\n',detail.columns)
detail.drop(labels = 'pay_way',axis = 1,inplace = True)
print('删除pay_way后detail的列索引为:','\n',detail.columns)
输出:
要删除某行数据,只需要将drop方法参数中的labels参数换成对应的行索引,将axis=0即可。
print('删除1-10行前detail的长度为:',len(detail))
detail.drop(labels = range(1,11),axis = 0,inplace = True)
print('删除1-10行后detail的列索引为:',len(detail))
输出:
3 描述分析DataFrame数据
描述性统计是用来概括、表述事物整体状况,以及事物间关联、类属关系的统计方法,通过几个统计值可表示一组数据的集中趋势和离散程度。
3.1 数值型特征的描述性统计
数值型特征的描述性统计包括:数据的完整情况、最大值、均值、中位数、最大值、四分位数、极差、标准差、方差、协方差和变异系数等
numpy库中已经有很多统计函数,pandas库基于numpy,自然也可以用这些统计函数对数据进行描述性统计。例如
通过函数求菜品销售价格的均值
print('订单详情表中amount(价格)的平均值为:', detail['amounts'].mean())
输出:
同时,pandas还提供了一个叫做describe的方法,能够一次性得出所有数值型特征的非空值数目、均值、四分位数和标准层。例如:
print('订单详情表counts和amounts两列的描述性统计为:\n',
detail[['counts','amounts']].describe())
输出:
另外,pandas还提供了与统计相关的主要方法,如下表
方法 | 说明 | 方法 | 说明 |
---|---|---|---|
min | 最小值 | max | 最大值 |
mean | 均值 | ptp | 极差 |
median | 中位数 | std | 标准差 |
var | 方差 | cov | 协方差 |
sem | 标准误差 | mode | 众数 |
skew | 样本偏度 | kurt | 样本峰度 |
quantile | 四分位数 | count | 非空值数目 |
descirbe | 描述统计 | mad | 平均绝对高差 |
3.2 类别型特征的描述性统计
类别型特征的分布状况,可以使用频数统计表,pandas库中实现频数统计的方法为values_counts。
对菜品销售数据中的菜品名aname进行频数统计,取排名靠前的10条
print('订单详情表dishes_name频数统计结果前10为:\n',
detail['dishes_name'].value_counts()[0:10])
输出:
除上述频率分布表外,pandas提供了category类,可以使用astype方法将目标特征的数据类型转换为category类型。如下:
detail['dishes_name'] = detail['dishes_name'].astype('category')
print('订单信息表dishes_name列转变数据类型后为:',detail['dishes_name'].dtypes)
输出:
describe方法除了支持传统数值型数据以外,还能够支持对category类型的数据进行描述性统计,4个统计量分别为列非空元素的数目、类别的数目、数目最多的类别和数目最多类别的数目,如下:
print('订单信息表dishes_name的描述统计结果为:\n',
detail['dishes_name'].describe())
输出:
4 小案例
4.1 查看餐饮数据的大小和维度
查看餐饮数据基本信息,主要从数据的维度、形状、元素的个数3个方面进行
from sqlalchemy import create_engine
import pandas as pd
# 数据库连接配置
username = 'root' # 替换为你的数据库用户名
password = '1234' # 替换为你的数据库密码
host = 'localhost' # 数据库地址
database = 'testdb' # 数据库名称
# 创建数据库连接
engine = create_engine(f'mysql+pymysql://{username}:{password}@{host}/{database}')
detail = pd.read_sql_table('meal_order_detail1',
con = engine)
order = pd.read_table('C:/Users/Lenovo/jupyter/data/meal_order_info.csv',
sep = ',',encoding = 'gbk')
user = pd.read_excel('C:/Users/Lenovo/jupyter/data/users.xlsx')
print('订单详情表的维度为:', detail.ndim)
print('订单信息表的维度为:', order.ndim)
print('客户信息表的维度为:', user.ndim)
print('订单详情表的形状为:', detail.shape)
print('订单信息表的形状为:', order.shape)
print('客户信息表的形状为:', user.shape)
print('订单详情表的元素个数为:', detail.size)
print('订单信息表的元素个数为:', order.size)
print('客户信息表的元素个数为:', user.size)
输出:
4.2 统计餐饮菜品销售状况
餐饮菜品的销售信息统计主要指统计菜品的销量、菜品的平均售价和热销菜品等信息。
print('订单详情表counts和amounts两列的描述性统计为:\n',
detail.loc[:, ['counts','amounts']].describe())
detail['order_id'] = detail['order_id'].astype('category')
detail['dishes_name'] = detail['dishes_name'].astype('category')
print('''订单信息表order_id(订单编号)与dishes_name(菜品名称)
的描述性统计结果为:''', '\n',
detail[['order_id','dishes_name']].describe())
输出:
4.3 剔除全为空值或者所有元素取值相同的列
## 定义一个函数去除全为空值的列和标准差为0的列
def dropNullStd(data):
beforelen = data.shape[1]
colisNull = data.describe().loc['count'] == 0
for i in range(len(colisNull)):
if colisNull[i]:
data.drop(colisNull.index[i],axis = 1,inplace =True)
stdisZero = data.describe().loc['std'] == 0
for i in range(len(stdisZero)):
if stdisZero[i]:
data.drop(stdisZero.index[i],axis = 1,inplace =True)
afterlen = data.shape[1]
print('去除的列的数目为:',beforelen-afterlen)
print('去除后数据的形状为:',data.shape)
dropNullStd(detail)
##使用dropNullStd函数对订单信息表操作
dropNullStd(order)
##使用dropNullStd函数对客户信息表操作
dropNullStd(user)
输出: