groupby大法
Pandas AIHub社区:https://www.kuxai.com/f/pandas
groupby参考链接:https://www.kuxai.com/post/141
元素选取参考链接:https://www.kuxai.com/post/126
相关链接:https://zhuanlan.zhihu.com/p/100064394
相关函数:
groupby
isin
map
dict(zip(list1, list2)):两个列表合为一个字典
apply
reset_index
fillna
data.rename(columns={'x1': 'y1', 'x2': 'y2'})
drop_duplicates()
实验数据
来源:2020农商行杯数据建模A题:01_ATM交易明细.xlsx(120M大小)
先导知识
Series数据结构
Series 是一个带有 名称 和 索引 的 一维数组,在 Series 中包含的数据类型可以是整数、浮点、字符串、Python对象等。其基础属性如下:
values:返回元素
index:返回索引
columns:返回列名
dtypes:返回类型
size:返回元素个数
ndim:返回维度数
shape:返回数据形状(行列数目)
实例:
读入:使用的数据读入后是一个Dataframe类型的结构体,通过iloc获取其第一列。
import pandas as pd
data = pd.read_csv(r'01_ATM交易明细_1.csv')
print('data is: ')
print(data.head
()) # head查看数据,默认输出5行
print('data type is: ', type(data))
ser = data.iloc[:, 0] # 获取data的的一列
ser = data.iloc[0] = data.iloc[0, :] # 获取data的第一行
print('ser is: ')
print(ser.head())
print('ser type is: ', type(ser))
结果截图:
属性操作:
print('ser values: ')
print(ser.values)
print('ser index: ')
print(ser.index)
# print(ser.columns)
# AttributeError: 'Series' object has no attribute 'columns'
print('ser dtypes: ')
print(ser.dtypes)
print('ser ndim: ')
print(ser.ndim)
print('ser shape: ')
print(ser.shape)
结果截图:
DataFrame数据结构
DataFrame 是一个带有索引的二维数据结构,每列可以有自己的名字,并且可以有不同的数据类型。你可以把它想象成一个 excel 表格或者数据库中的一张表,DataFrame 是最常用的 Pandas 对象。其基础属性:
values:返回元素
index:返回索引
columns:返回列名
dtypes:返回类型
size:返回元素个数
ndim:返回维度数
shape:返回数据形状(行列数目)
属性操作:
import pandas as pd
data = pd.read_csv(r'01_ATM交易明细_1.csv')
print('data is: ')
print(data.head()) # head查看数据,默认输出5行
print('data type is: ', type(data))
print('data values: ')
print(data.values)
print('data index: ')
print(data.index)
print('data columns: ')
print(data.columns)
print('data dtypes: ')
print(data.dtypes)
print('data ndim: ')
print(data.ndim)
print('data shape: ')
print(data.shape)
结果截图:
object就是字符串
groupby作用
在日常的数据分析中,经常需要将数据根据某个(多个)字段划分为不同的群体(group)进行分析,如电商领域将全国的总销售额根据省份进行划分,分析各省销售额的变化情况,社交领域将用户根据画像(性别、年龄)进行细分,研究用户的使用情况和偏好等。
注意groupby之后的数据类型,它不再是一个dataframe,而是一个GroupBy对象,我们后面函数的任何操作都是基于这个对象的。
实战
统计某个设备某天的交易额和交易次数
以设备编号和交易日期字段为分组标准后,再对交易金额进行聚合操作-求和,对于交易次数可以通过对任一字段作计数操作,然后重命名即可。
代码如下:
dfGrpBy = data.groupby(['设备编号', '交易日期'])
print('dfGrpBy type is: ', type(dfGrpBy))
databyid_date = dfGrpBy.agg({'交易金额': 'sum', '交易时间': 'count'})
# 对DataFrameGroupBy对象使用聚合操作,对交易金额求和,对交易时间计数
# 在这里计数的作用是可以得到某个设备某个交易日期(这一天)交易的次数
databyid_date = databyid_date.rename(columns={'交易金额': '总交易额', '交易时间': '交易次数'})
print('databyid_date is: ')
print(databyid_date)
databyid_date = databyid_date.reset_index()
print('databyid_date after reset_index is: ')
print(databyid_date)
结果截图:
上面的代码不是很简洁,需要reset_index,其实可以在使用分组时设置参数as_index=False,如下:
dfGrpBy = data.groupby(['设备编号', '交易日期'], as_index=False)
结果如下:
即使用as_index=False后跟原先使用reset_index()的效果一样。
使用as_index=False表示不要使用在原先数据中的索引,而是重新从0开始排索引。而reset_index()顾名思义就是重置索引,其实就是加了一列新的索引,这个索引是从0顺序开始的。
统计某个设备的工作天数
问题关键在于交易日期的去重。
方法一
先以设备编号、交易日期分组,这样就间接把交易日期去重了,然后再以设备编号分组,对交易日期字段进行计数即可。
dfGrpBy = data.groupby(['设备编号', '交易日期'], as_index=False)
# 这里无所谓,操作其它字段也没问题,采用sum\mean\std\var等聚合方法
# 也都行,这里主要就是把交易日期间接去重。
dataNew = dfGrpBy.agg({'交易时间': 'count'})
print('间接去重交易日期:')
print(dataNew)
dfGrpBy = dataNew.groupby(['设备编号'], as_index=False)
dataNew = dfGrpBy.agg({'交易日期': 'count'})
dataNew = dataNew.rename(columns={'交易日期': '工作天数'})
print('某设备工作天数如下:')
print(dataNew)
结果截图:
方法二-使用apply
applyt过程图解:https://www.kuxai.com/post/141
简单的说,它是将经过分组后的每一组依次传入apply对应的处理函数,然后进行相依的操作,最后再将结果聚合起来。
global i
i = 1
## 计算每个ATM总的工作天数
def calcuWkDay(x):
global i
y = x['交易日期'].drop_duplicates() # 先把日期去重
y = y.count()
if i == 1: # 只是想看一下传入的x是什么东西(只打印第一次)
print('传入的参数是:')
print(x)
i = i + 1
return y
dfGrpBy = data.groupby(['设备编号'], as_index=False)
dataNew = dfGrpBy.apply(calcuWkDay)
print('某设备工作天数如下:')
print(dataNew)
结果截图:
上面的代码确实实现了工作天数的统计,返回的是一个Series类型。但设备编号好像没有了,所以修改一下代码,实现与设备编号相对应。
## 计算每个ATM总的工作天数
def calcuWkDay(x):
deviceID = x['设备编号'] # 结果是一个Series,即包括了前面的索引
deviceID = deviceID.values[0]
y = x['交易日期'].drop_duplicates() # 先把日期去重
y = y.count()
return (deviceID, y)
dfGrpBy = data.groupby(['设备编号'], as_index=False)
dataNew = dfGrpBy.apply(calcuWkDay)
print('某设备工作天数如下:')
print(dataNew)
结果截图:
函数isin-提取交易类型为存取款的记录
dataNew = data[data['交易类型'].isin(['存款', '取款'])]
print('取款和存款的流水如下:')
print(dataNew.iloc[0:10, :])
结果截图:
函数map的应用-不同交易类型有不同的花费时间
spendTime = {"存款":3, "取款": 3, "转账": 5, "查询": 1} # 单位:min
dataNew = data
dataNew['花费时间'] = dataNew['交易类型'].map(spendTime)
print('根据交易类型赋予不同交易时间:')
print(dataNew)
# 因为map需要传入的参数是一个字典,所以有时可以通过下面的方式将
# 两个列表合为一个字典
# map1 = dict(zip(list1, list2))
结果截图:
2020:8:28:21:55
把某列作为索引/index
print('data is: ')
print(data.head())
# 将设备编号列设为索引(默认同时删除对应的数据)
dataNew = data.set_index(['设备编号'])
print('dataNew is: ')
print(dataNew.head())
# 将设备编号列设为索引(不删除对应的数据)
dataNew = data.set_index(['设备编号'], drop=False)
print('dataNew is: ')
print(dataNew.head())
结果截图:
新需求
如下:https://www.kuxai.com/post/139
先构造如图类型的表,如下:
print('data is: ')
print(data.head())
dfGrpBy = data.groupby(['设备编号', '交易类型'], as_index=False)
dataNew = dfGrpBy.agg({'交易日期': 'count'})
dataNew = dataNew.rename(columns={'交易日期': '交易次数'})
print('dataNew is: ')
print(dataNew)
结果截图:
方法如下:
def convert(x):
y = dict(zip(list(x['交易类型']), list(x['交易次数'])))
y = pd.DataFrame(data=y, index=[0]) # 这里必须指明index,不然报错
return y
dataNew = dataNew.groupby(['设备编号']).apply(convert)
print('dataNew before reset_index is: ')
print(dataNew.head())
dataNew = dataNew.reset_index()
print('dataNew after reset_index is: ')
print(dataNew.head())
del dataNew['level_1'] # 删除level_1这一列,这一列全为0
dataNew = dataNew.fillna(0)
print('dataNew after fillnan is: ')
print(dataNew.head())
结果截图:
在做比赛时用到了地址解析
获取某个位置的经度纬度
import requests
#使用高德API
def geocodeG(address):
par = {'address': address, 'key': '6e2f236cefff29925caf4650caccd20f'}
base = 'http://restapi.amap.com/v3/geocode/geo'
response = requests.get(base, par)
answer = response.json()
GPS=answer['geocodes'][0]['location'].split(",")
return GPS[0],GPS[1]
#使用百度API
def geocodeB(address):
base = url = "http://api.map.baidu.com/geocoder?address=" + address + "&output=json&key=f247cdb592eb43ebac6ccd27f796e2d2"
response = requests.get(base)
answer = response.json()
return answer['result']['location']['lng'],answer['result']['location']['lat']
addr = geocodeG('江北区观音桥步行街7号')
print(addr)