用Pandas处理统计ChinaVis2017年挑战赛2的数据

初学pandas打算用一些数据来练练手,于是选择了ChinaVis2017年挑战赛2的数据,由于本人初学知识和经验都掌握的不足,请大家多多批评指正。

  ChinaVis2017年挑战赛2的数据信息为重庆市网吧上网信息的记录,它分为两部分,一部分为网吧信息(网吧信息.csv,后面我就简称表1),其记录的是重庆市各大网吧的编号、网吧名称、地理坐标(经纬度),其信息共3590条;另一部分为用户的上网信息(hydata_swjl_0.csv和hydata_swjl_1.csv,后面我统一简称表2),它记录的是网吧编号、顾客姓名、顾客性别、上线时间、下线时间、所在地域编号、出生日期,其信息共2000003条;根据17年挑战赛2获奖第一名作品的答卷,我总结了以下九条需要统计处理的信息:

(1)每个网吧上网人数

(2)筛选出上网人数大于10的网吧

(3)年龄大于68岁以上的(一般大于68岁的人不会上网,也不可能待在网吧,故怀疑是假身份证)

(4)每个网吧未成年上网的人数

(5)24点以后上线的人数

(6)单次连续上网超过48小时的人数(可能是假身份证)

(7)外来人口上网次数

(8)每个网吧外来人口上网的高峰时间段

(9)籍贯和上网时间均相同的人(可能是团伙)

下面我就来说说我在处理过程中所用到Pandas的哪些库和方法。

1.数据的统计与合并

数据的统计与合并几乎在处理这九项数据时都会用到,例如用统计“每个网吧的上网人数”为例:首先,我们对表2进行处理,将表中所有网吧编号进行计数,然后根据表1中的网吧编号在表1中新增一列用于存放与之对应的网吧上网次数这一项,具体代码如下:

da = pd.read_csv('C:\\Users\\Administrator\\Desktop\\2017\\hydata_swjl_0.csv', sep=',', low_memory=False, header=0)
wb = pd.read_csv('C:\\Users\\Administrator\\Desktop\\2017\\wangba.csv', sep=',', low_memory=False, header=0)
#打开表1和表2,pd.read_csv(“路径”,“以,为分隔符”,“采用较大的数据类型来储存”)
wb = wb.drop(['TITLE', 'Unnamed: 4'], axis=1)
# 计算每个网吧的上网人数
M = da['SITEID'].value_counts()
dict_m = {'SITEID': M.index, 'cishu': M.values}
df_m = pd.DataFrame(dict_m)
merged1 = pd.merge(wb, df_m, how='left', left_on=['SITEID'], right_on=['SITEID'])
merged1.fillna(0, inplace=True)
merged1[['cishu']] = merged1[['cishu']].astype(int)

da['SITEID']选中da对象中的SITEID(网吧编号)这个选项,然后通过.value_counts()这个方法进行统计网吧编号重复的次数。需要注意的是在表1中存在编号的网吧,在表2中不一定有上网信息,所以在通过网吧编号进行匹配时,存在有的项无法对应的问题,系统会自动补为缺失值NAN,但我们需要的数据中不能有空值,所以我们通过merged1.fillna(0, inplace=True)将对象中为NAN的值设置为0

运行结果如下:

fillna填充缺失值的具体用法如下:

df.fillna("missing")  #此数据已被填充
df.fillna(method='pad')  #用前一个数据代替NaN:method='pad'
df.fillna(method='bfill',limit=1)  #与pad相反,bfill表示用后一个数据代替NaN
df.fillna(df.mean()) #使用平均数或者其他描述性统计量来代替NaN
df.fillna("missing",inplace=True)  #df 原数据已被 missing 填充

 pd.merged()的参数:

on:列名,join用来对齐的那一列的名字,用到这个参数的时候一定要保证左表和右表用来对齐的那一列都有相同的列名。

left_on:左表对齐的列,可以是列名,也可以是和dataframe同样长度的arrays。

right_on:右表对齐的列,可以是列名,也可以是和dataframe同样长度的arrays。

left_index/ right_index: 如果是True的haunted以index作为对齐的key

how:数据融合的方法。

sort:根据dataframe合并的keys按字典顺序排序,默认是,如果置false可以提高表现。

 2.筛选与过滤

筛选与过滤的一些方法:

isin([])

当我们需要去掉不止一个值,这个时候只需要在isin([])的列表中添加。

df[(da['cishu'].isin([493,277]))] 

有时候并不只是考虑某一列,还需要考虑另外若干列的情况。

df[(da['cishu'].isin([493]))&(da['SITEID'].isin([50010510000039]))]

过滤掉某个范围的值:

只将数据中上网次数大于等于10的网吧赋给对象t

t = merged1[merged1['cishu'] >= 10]

用以上方法,我们可以筛选出上网次数大于10的网吧,以及统计出上网人员年龄大于68岁的人员,然后把每个网吧上网人员年龄大于68岁的新增为一列,具体代码如下:

#统计 age>68岁的人数
da[['BIRTHDAY']] = da[['BIRTHDAY']].astype(int)
Be_age = da[da['BIRTHDAY'] <= 19500000]
Be_age = Be_age[Be_age['BIRTHDAY'] >= 19000000]
Be_age = Be_age.drop(['PERSONID'], axis=1)
Un_age = da[da['BIRTHDAY'] >= 19990000]
Un_age = Un_age[Un_age['BIRTHDAY'] <= 20160000]
M = Be_age['SITEID'].value_counts()
dict_m = {'SITEID': M.index, 'beyond_age_count': M.values}
df_m = pd.DataFrame(dict_m)

merged2 = pd.merge(t, df_m, how='left', left_on=['SITEID'], right_on=['SITEID'])
merged2.fillna(0, inplace=True)
merged2[['beyond_age_count']] = merged2[['beyond_age_count']].astype(int)
print(merged2)

 运行结果如下:

去掉一些原数据中你不想要的列的方法:

Be_age = Be_age.drop(['PERSONID'], axis=1)

统计合并时,表2中的 PERSONID列对我们的统计结果没用,所以为了结果看起来简洁,我们用pandas.DataFrame.drop的方法把他去掉

drop_duplicates(inplace=True)是直接对原dataFrame进行操作。
s = t.drop_duplicates(inplace=False) 则,t的内容不发生改变,s的内容是去除重复后的内容

通过以上这些方法,我们已经能够将每个网吧上网人数、筛选出上网人数大于10的网吧、年龄大于68岁以上的(可能是假身份证)、每个网吧未成年上网的人数、24点以后上线的人数,这些项筛选出来并加入到我们要用的列后边

3.对时间的处理

在表2 文件中有每个用户上网的上线时间和下线时间列名分别为:OFFLINETIME和ONLINETIME。但是他们都是以字符串或则int类型存储的,所以要对他们进行统计的话,我们必须将其处理为Pandas识别的时间类型,其方法如下:

① 时间字符串转化成时间戳
将时间字符串转化成时间戳分为两步:
第一步:将时间字符串转换成时间元组
第二步:将时间元组转换成时间戳类型

timestamp1 = da['ONLINETIME'].apply(lambda x: time.mktime(time.strptime(x, '%Y%m%d%H%M%S')))

其中,strptime函数是将字符串按照后面的格式转换成时间元组类型;mktime函数则是将时间元组转换成时间戳

② 将时间戳转换成可读字符串
第一步:用localtime将时间戳转换成local_time,时间元组
第二步:用strftime将local_time转换成可读字符串

timestamp = 1.521708e+09 
time_local = time.localtime(timestamp) time_local 
# 输出: 
# time.struct_time(tm_year=2018, tm_mon=3, tm_mday=22, tm_hour=16, tm_min=40, tm_sec=0, tm_wday=3, tm_yday=81, tm_isdst=0) time.strftime('%Y/%m/%d %H:%M:%S',time_local) 
# 输出: 
'2018/03/22 16:40:00'

在这里我使用了①,将字符串转换为时间戳的方法,然后通过计算两列时间戳之间的差值为判断该用户是否连续上网时间超过48小时(注意:两个时间戳进行差运算计算出的数值是一个以秒为单位的,48小时=172800秒),代码如下:

# 单次连续上网超过48小时的人数
da[['ONLINETIME']] = da[['ONLINETIME']].astype(str)
da[['OFFLINETIME']] = da[['OFFLINETIME']].astype(str)
timestamp1 = da['ONLINETIME'].apply(lambda x: time.mktime(time.strptime(x, '%Y%m%d%H%M%S')))
timestamp2 = da['OFFLINETIME'].apply(lambda x: time.mktime(time.strptime(x, '%Y%m%d%H%M%S')))
#timestamp2 = pd.to_datetime(da['ONLINETIME'], format='%Y%m%d%H%M%S')
#time_local = {time.localtime(x) for x in timestamp1}
da['timestamp'] = timestamp2-timestamp1
Is_time = da[da['timestamp'] >= 172800]

M = Is_time['SITEID'].value_counts()
dict_m = {'SITEID': M.index, 'over_time': M.values}
df_m = pd.DataFrame(dict_m)
merged4 = pd.merge(merged3, df_m, how='left', left_on=['SITEID'], right_on=['SITEID'])
merged4.fillna(0, inplace=True)
merged4[['over_time']] = merged4[['over_time']].astype(int)
print(merged4)

 其运行结果如下:

其中 time.localtime这个方法用于对时间进行转换后,还可以通过time.tm_hours、time.tm_seconds等等,将时间数据中的具体时或秒或天给摘取出来。不过由于tm_hours似乎不能处理整列的信息只能单个的处理(其实我也不太清楚,网上百度了但没找到还望知道的大神指点),所以我采用了一个笨办法:就是先将上线时间转换为float型,然后除10000,再转为int,再%100,这样我们就能得到整列的每个人员上网时间中的小时提取出来,代码如下:

da[['ONLINETIME']] = da[['ONLINETIME']].astype(str)
a = da[['ONLINETIME']].astype(float)
b = a / 10000
b = b.astype(int)
c = b % 100
da['late_time'] = c
late_time = da[(da['late_time'] >= 0) & (da['late_time'] <= 4)]
#筛选出凌晨0点到4点上网的人员
M = late_time['SITEID'].value_counts()
dict_m = {'SITEID': M.index, 'late_time': M.values}
df_m = pd.DataFrame(dict_m)

然后通过values_count()计算出这些时间点出现的次数,其中出现的最多的可以判定其为上网的高峰时段,其代码如下:

#外来人口上网的高峰时间
foreign2 = da[(da['AREAID'] < 510200) | (da['AREAID'] >= 510299)]
M = foreign2['late_time'].value_counts()
dict_m = {'late_time': M.index, 'cishu': M.values}
df_m = pd.DataFrame(dict_m)
print(df_m)

其运行结果如下:

这里我只显示了前十的数据,从中我们不难看出:上网人数排前三时间点是19点、18点、20点,其次是13点、14点,再然后21点、12点、15点。从这10条时间点我们不难推测出:上网人数最大的高峰期是 从晚上18点开始,19点到最高峰,20点开始回落,从中午12点开始到13点达到最高峰,14点开始有所回落。故此的出结论:外来人员上网的高峰时段分为午高峰和晚高峰,午高峰是12点-14点,晚高峰是18点-21点。由于这些时间段比较符合我们人的作息规律,所以我认为结果还是比较正确。

5、数据中列的合并

为了统计上网时间、籍贯和网吧地址都相同的人,我需要将以这三个为索引的列合并成为一列,用到的方法如下:

dataframe["newColumn"] = dataframe["age"].map(str) + dataframe["phone"] + dataframe["address”]

当然能合并就能拆分: 

crime1 = crime['new_column'].str.split('_', expand=True) 

处理的具体代码如下:

da['split'] = "_"
da['new_column'] = da['SITEID'].map(str)+da['split']+da['ONLINETIME'].map(str)+da['AREAID'].map(str)
crime = da['new_column'].value_counts(sort = False)
dict_m = {'new_column': crime.index, 'crime': crime.values}
crime = pd.DataFrame(dict_m)
crime1 = crime['new_column'].str.split('_', expand=True)
dict_m = {'SITEID': crime1[0].values}
crime1 = pd.DataFrame(dict_m)
crime['SITEID'] = crime1
crime = crime.drop(['new_column'], axis=1)
crime = crime[crime['crime'] >= 3]
crime = crime.groupby('SITEID').sum()
dict_m = {'SITEID': crime['crime'].index, 'crime': crime['crime'].values}
crime_end = pd.DataFrame(dict_m)

运行结果:

 

 

 

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值