Pandas:大数据处理

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)
赞赏