【干货+福利】情侣、基友、渣男和狗-基于SynchroTrap+LPA算法的团伙账户挖掘

c0442874b94edff01998e9106f79c0f4.jpeg

本文目的:把经常一起行动的人找出来,并划分成一个Group,仅利用时间关系,无需其他介质

上一期的文章,没有解决相隔时间近但是不在同一个5min切片内的的问题,为了解决这个问题,我研究了一些关于时序的关联规则算法,包括GSP、Prefixspan、FreeSpan等,在我们这个场景里面,不能很好的解决。我们用SynchroTrap+LPA这两个,就能非常完美的解决,SynchroTrap算法用来构图,LPA用来分群。有人说,没有介质会不会不准,其实大家可以想象下,在食堂吃饭的时候,有没有连续几天都是同一个你不认识的人排在你的前面?

SynchroTrap+LPA比关联规则算发现较大规模的群体,本文中,我们仅仅利用校园卡消费明细数据和这两个算法,比如班级、室友等,也能发现情侣、基友、闺蜜、渣男、渣女等非常有意思的模式我们一步步来看看怎么做的。

一、同步关系构建

首先,我们需要把时间序列的数据,处理成关系数据,具体的逻辑参考文章基于同步行为的反欺诈算法SynchroTrap实现细节,里面写的非常详细,下面我们用这个校园卡的消费数据来实现这个过程。没学明白的可以巩固下。

1、读取数据

# 数据读取
import pandas as pd
import os
pd.set_option('display.max_columns', None)
os.chdir('/Users/wuzhengxiang/Documents/DataSets/students')
data1 = pd.read_csv("data1.csv", encoding="gbk")
data2 = pd.read_csv("data2.csv", encoding="gbk")
data3 = pd.read_csv("data3.csv", encoding="gbk")
data1.columns = ['序号', '校园卡号', '性别', '专业名称', '门禁卡号']
data2.columns = ['流水号', '校园卡号', '校园卡编号', '消费时间', '消费金额', '存储金额', '余额', '消费次数', '消费类型', '消费项目编码', '消费项目序列号', '消费操作编码', '操作编码', '消费地点']
data3.columns = ['序号', '门禁卡号', '进出时间', '进出地点', '是否通过', '描述']
print(data1.head(3))
print(data2.head(3))
print(data3.head(3))


# 数据匹配,匹配上性别数据,分析更直观
data2 = data2.merge(data1[['校园卡号','性别']],on='校园卡号')
data2['校园卡号'] = data2['校园卡号'].apply(lambda x: str(x))+'-'+data2['性别']

我们主要使用的是data2的数据,data1的数据仅仅使用了性别这个字段,data3这个字段在这里没有使用。数据格式长下面这个样子。

b43faf87c8e4ef274876b9a83aeb95cd.png

2、时间切片处理

需要把时间切片,这里使用的是5分钟一个片段,一般来说,两个消费的关系,大概就是排队刷卡的时间,5分之之内,比较合理。

import datetime
'''
函数功能
时间格式调整
'2019/4/20 20:17'=>'2019-04-20 20:17:00'
'''
def st_pt(x):
     return str(datetime.datetime.strptime(x, "%Y/%m/%d %H:%M"))
'''
函数功能
时间切片
5分钟一个切片,且每个时间会切成两个片段
Time2Str('2021-11-16 15:51:39' )
'2021-11-16 15:51:39'  => '2021111615(10);2021111615(11)'
'''
def Time2Str(tsm):
    t0 = datetime.datetime.fromisoformat(tsm)
    t1 = t0+datetime.timedelta(days=0, hours=5/60)
    str1 = t0.strftime("%Y%m%d%H")+'(' +str(round(int(t0.minute/5))).rjust(2,'0')+')'
    str2 = t1.strftime("%Y%m%d%H")+'(' +str(round(int(t1.minute/5))).rjust(2,'0')+')'
    return str1+';'+str2

函数好了后,对数据进行处理

# 开始数据处理
df = data2
df['消费时间'] = df['消费时间'].apply(st_pt)
df['tsm']     = df['消费时间'].apply(Time2Str)


# 数据分裂,一行变两行
df = df.set_index(["校园卡号", "消费时间",'消费地点'])["tsm"].str.split(";", expand=True)\
    .stack().reset_index(drop=True, level=-1).reset_index().rename(columns={0: "tsm"})
print(df)
# 可以看到,处理后的格式如下
    校园卡号                 消费时间    消费地点             tsm
0       180641-女  2019-04-01 10:00:00   红太阳超市  2019040110(00)
1       180641-女  2019-04-01 10:00:00   红太阳超市  2019040110(01)
2       181021-女  2019-04-01 10:00:00    第一食堂  2019040110(00)
3       181021-女  2019-04-01 10:00:00    第一食堂  2019040110(01)
4       181036-女  2019-04-01 10:00:00    第一食堂  2019040110(00)

3、匹配构图

切片处理好后,就开始匹配构图了,我们的切片匹配后,还需要加个时间限制,上一步操作仅仅把时间限制在10分钟内,因此为了得到5分钟,还需要作差筛选

# 数据匹配,加入时间约束和地点约束
df_0 = pd.merge(df,df,on =['tsm','消费地点'],how='inner')
df_0.shape


# 排除 自己和自己匹配的数据
df_1 = df_0[df_0['校园卡号_x']!=df_0['校园卡号_y']]


# 时间作差,大于5分钟的排除
df_1['diff'] = (pd.to_datetime(df_1['消费时间_x'])-pd.to_datetime(df_1['消费时间_y'])).dt.seconds/60
df_1 = df_1[df_1['diff']<=5]


# 提取小时 按共同出现的小时计数
df_1['date'] = df_1['tsm'].apply(lambda x :x[0:10])


#统计两两关联的次数,这里比较简单,不按天,也不计算相似度了
df_2 = df_1.groupby(['校园卡号_x','校园卡号_y']).agg({'date': pd.Series.nunique}).reset_index()


# 降序排列
df_2 = df_2.sort_values(by='date',ascending=False) 


 校园卡号_x    校园卡号_y  date
1031801  181293-女  181268-女    68
1008844  181268-女  181293-女    64
343421   180399-女  183665-女    60
309673   180363-女  181876-女    58
1967448  182392-女  182377-女    58
          ...       ...   ...
1509502  181840-男  181732-女     1
1509501  181840-男  181731-女     1
1509499  181840-男  181729-女     1
1509497  181840-男  181727-女     1
1645283  181999-男  183585-女     1


df_2.shape
(3290566, 3)

4、阈值确定

图数据好了,但是我们看有300多万,很多一个月就一次,这个肯定就是巧合数据,每次你去食堂排队,排队前后的,和你的时间间隔就比较少,但是偶尔的关联,并不是我们想要的,所以我们要进一步筛选这里我确定20次,数据瞬间从300万到1924了。

# 给关系加阈值,大于20次的算是比较强的关联了
df_3 = df_2[df_2['date']>=20]


df_3.shape
(1924, 3)


df_3
 校园卡号_x    校园卡号_y  date
1031801  181293-女  181268-女    68
1008844  181268-女  181293-女    64
343421   180399-女  183665-女    60
309673   180363-女  181876-女    58
1967448  182392-女  182377-女    58
          ...       ...   ...
156386   180173-女  180109-女    20
271647   180320-女  183546-女    20
2884196  183692-女  180377-女    20
66844    180085-女  180394-女    20
1801477  182194-女  182212-女    20

二、LPA分群

这里我们直接调用networkx库里的asyn_lpa_communities算法,对上面的关系数据进行分群,每个学生都会分布到一个独立的群体里面。为了得到不同程度的关系,我们可以调节关联次数这个阈值。大家可以多测试几个看看。

1、算法分群

import matplotlib.pyplot as plt
import networkx as nx
from networkx.algorithms.community import asyn_lpa_communities as lpa




# 数据格式转换
tuples = [tuple(x) for x in df_3[['校园卡号_x','校园卡号_y']].values]
G = nx.DiGraph()
G.add_edges_from(tuples)


# LPA本身不稳定,因此,社群存在小范围波动
com = list(lpa(G))
print('社区数量',len(com))
449


# 打印分群数据 仅展示部分数据
for i in com:
    print(i)
{'181559-男', '181568-男', '181556-男', '181564-男'}
{'181013-女', '181042-女', '180624-男'}
{'180851-女', '181461-男', '180780-女', '180856-女'}
{'180105-女', '180164-女', '180198-女', '180118-女'}
{'183401-女', '183385-女'}
{'181364-男', '181528-男'}
{'182031-男', '182029-男', '182016-男', '182033-男'}
{'182039-男', '182042-男', '182041-男', '182040-男'}
{'183706-女', '183691-女'}
{'180449-女', '180491-女', '180489-女', '180455-女', '180456-女', '180476-女'}
{'181013-女', '181042-女', '180624-男'}
{'182283-女', '182244-女'}  
{'181623-男', '183847-女', '181597-男'}

2、明细数据获取

为了进行比较明细的分析,我们可以挑一些我们比较感兴趣的关系对,进行明细数据分析

# 渣女组合
result = data2[(data2['校园卡号']=='181623-男') | (data2['校园卡号']=='183847-女') | \
      (data2['校园卡号']=='181597-男')]
# 情侣组合
result = data2[(data2['校园卡号']=='181774-男') | (data2['校园卡号']=='183898-女')]
result = result.sort_values(by='消费时间')


# 用户 -时间-地点关系数据
result = df_1[((df_1['校园卡号_x']=='181774-男') & (df_1['校园卡号_y']=='183898-女')) | \
              ((df_1['校园卡号_y']=='181774-男') & (df_1['校园卡号_x']=='183898-女'))]


# 数据整理保存备用
result.to_csv('resualt.csv',index=False,header=True)

3、数据可视化

其实数据不够直观,我们可以进一步去做可视化

我们可以构建一个学生-时间地点-学生的 图,如下面,表示【181774-男】 和  【183898-女】 在20190415的11点出现过,如果你看明细数据,他们出现的时间 2019/4/15 11:39

209556602d90618eb8396b507595ed5a.png

在看看下面,是两个人所有的在5分钟之内的链接,非常紧密和频繁,一看就是很恩爱的情侣。

dfa8b1bd3590dd45e53f9adaf49b7b9f.png

我们再看一个渣男渣女组合,大家可以自己去看看呢,上面的绘图,可以使用https://app.flourish.studio这个网站。

用matplotlib.pyplot绘图试试,简直没法看

# 下面是画图 
pos = nx.spring_layout(G) # 节点的布局为spring型
NodeId    = list(G.nodes())
node_size = [G.degree(i)**1.2*90 for i in NodeId] # 节点大小


plt.figure(figsize = (8,6)) # 图片大小
nx.draw(G,pos, with_labels=True, 
        node_size =node_size, 
        node_color='w', 
        node_shape = '.'
        )
'''
node_size表示节点大小
node_color表示节点颜色
with_labels=True表示节点是否带标签
'''
color_list = ['pink','orange','r','g','b','y','m','gray','black','c','brown']


for i in range(len(com)):
    nx.draw_networkx_nodes(G, pos, nodelist = com[i], 
                           node_color=color_list[i+2],  
                           label=True)
plt.show()

91aa5bbedc1b1e28b898612bda95d63d.jpeg

三、数据结论

1、反欺诈应用

通过上面的案例,我们可以看到SynchroTrap+LPA算法,对于无介质的欺诈用户进行关联和分群,是非常完美的一个组合,大家可以在自己的数据里面试试,可能有意外惊喜。

2、一个题外话

由此,我们可以联想到滴滴数据泄露为什么受到如此重视?滴滴拥有大量的行程数据,特别是再加上时序,结合前沿的大数据的分析方法,可以发现让人震惊的结论。

一个人长期往返于军工学校的实验室和某个建筑物,且建筑在地图上没有标名字,那很可能是个重要的军事基地

一个人经常往返于某发射基地和某建筑物,那某个建筑,可能是导弹生产工厂,再进行重点监控

将车流、人流、现状用地信息叠加,地图空白处,可能是国家重要的军火粮食仓库

分析保密单位工作性质,以及员工或访客上下班的滴滴数据,那么就可以推算其工作性质

某个人物,和另一个人经常同行,那同行人重点监控,可能发现大秘密

A-B,B-C,C-D存在大规模的连接,推断出他们是一个单位的,关键时刻进行一锅端

·······

诸如此类,大家可以举出无穷的例子,利用已知推导出疑似的未知,这会暴露很多涉及国家安全的基础设施,还会暴露很多涉及国家安全的人物,非常可怕,并且出行数据,都是真实轨迹,推断具有极大的准确性。

------

赠书福利!

 
 
1、赠书:贝叶斯算法与机器学习*5
2、赠书规则:点击下方公众号回复【贝叶斯】即可参与红包抽奖决定赠书名额

3、书籍介绍
本书从贝叶斯理论的基本原理讲起,逐步深入算法、机器学习、深度学习,并配合项目案例,重点介绍了基于贝叶斯理论的算法原理,及其在机器学习中的应用。
本书分为10章,涵盖了贝叶斯概率、概率估计、贝叶斯分类、随机场、参数估计、机器学习、深度学习、贝叶斯网络、动态贝叶斯网络、贝叶斯深度学习等。本书涉及的应用领域包含机器学习、图像处理、语音识别、语义分析等。本书整体由易到难,逐步深入,内容以算法原理讲解和应用解析为主,每节内容辅以案例进行综合讲解。
本书内容通俗易懂,案例贴合实际,实用性强,适合有一定算法基础的读者进阶阅读,也适合其他人作为爱好阅读。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值