Python 从dataframe创建矩阵以显示连通性

Python 从dataframe创建矩阵以显示连通性,python,matrix,Python,Matrix,我在熊猫数据框中有这种格式的数据: Customer_ID Location_ID Alpha A Alpha B Alpha C Beta A Beta B Beta D 我想研究客户的流动模式。我的目标是确定客户最常光顾的地点集群。我认为以下矩阵可以提供此类信息: A B C D A 0 2 1 1 B 2 0 1

我在熊猫数据框中有这种格式的数据:

Customer_ID  Location_ID
Alpha             A
Alpha             B
Alpha             C
Beta              A
Beta              B
Beta              D
我想研究客户的流动模式。我的目标是确定客户最常光顾的地点集群。我认为以下矩阵可以提供此类信息:

   A  B  C  D
A  0  2  1  1
B  2  0  1  1
C  1  1  0  0
D  1  1  0  0
在Python中如何做到这一点


我的数据集相当大(几十万个客户和大约一百个地点)。

这里有一种方法考虑了访问的多样性(例如,如果客户X同时访问LocA和LocB两次,他将为最终矩阵中的相应位置贡献
2

想法:

  • 对于每个地点,按客户统计访问次数
  • 对于每个位置对,找到同时访问这两个位置对的每个客户的最小访问次数之和
  • 使用
    unstack
    和cleanup
  • Counter
    在这里运行得很好,因为计数器支持许多自然算术运算,如
    add
    max

    import pandas as pd
    from collections import Counter
    from itertools import product
    
    df = pd.DataFrame({
        'Customer_ID': ['Alpha', 'Alpha', 'Alpha', 'Beta', 'Beta'],
        'Location_ID': ['A', 'B', 'C', 'A', 'B'],
        })
    
    
    ctrs = {location: Counter(gp.Customer_ID) for location, gp in df.groupby('Location_ID')}
    
    
    # In [7]: q.ctrs
    # Out[7]:
    # {'A': Counter({'Alpha': 1, 'Beta': 1}),
    #  'B': Counter({'Alpha': 1, 'Beta': 1}),
    #  'C': Counter({'Alpha': 1})}
    
    
    ctrs = list(ctrs.items())
    overlaps = [(loc1, loc2, sum(min(ctr1[k], ctr2[k]) for k in ctr1))
        for i, (loc1, ctr1) in enumerate(ctrs, start=1)
        for (loc2, ctr2) in ctrs[i:] if loc1 != loc2]
    overlaps += [(l2, l1, c) for l1, l2, c in overlaps]
    
    
    df2 = pd.DataFrame(overlaps, columns=['Loc1', 'Loc2', 'Count'])
    df2 = df2.set_index(['Loc1', 'Loc2'])
    df2 = df2.unstack().fillna(0).astype(int)
    
    
    #      Count
    # Loc2     A  B  C
    # Loc1
    # A        0  2  1
    # B        2  0  1
    # C        1  1  0
    

    如果您想忽略多重性,请将
    计数器(gp.Customer\u ID)
    替换为
    计数器(set(gp.Customer\u ID))

    以下是一种考虑访问多重性的方法(例如,如果客户X同时访问LocA和LocB两次,他将为最终矩阵中的相应位置贡献
    2

    想法:

  • 对于每个地点,按客户统计访问次数
  • 对于每个位置对,找到同时访问这两个位置对的每个客户的最小访问次数之和
  • 使用
    unstack
    和cleanup
  • Counter
    在这里运行得很好,因为计数器支持许多自然算术运算,如
    add
    max

    import pandas as pd
    from collections import Counter
    from itertools import product
    
    df = pd.DataFrame({
        'Customer_ID': ['Alpha', 'Alpha', 'Alpha', 'Beta', 'Beta'],
        'Location_ID': ['A', 'B', 'C', 'A', 'B'],
        })
    
    
    ctrs = {location: Counter(gp.Customer_ID) for location, gp in df.groupby('Location_ID')}
    
    
    # In [7]: q.ctrs
    # Out[7]:
    # {'A': Counter({'Alpha': 1, 'Beta': 1}),
    #  'B': Counter({'Alpha': 1, 'Beta': 1}),
    #  'C': Counter({'Alpha': 1})}
    
    
    ctrs = list(ctrs.items())
    overlaps = [(loc1, loc2, sum(min(ctr1[k], ctr2[k]) for k in ctr1))
        for i, (loc1, ctr1) in enumerate(ctrs, start=1)
        for (loc2, ctr2) in ctrs[i:] if loc1 != loc2]
    overlaps += [(l2, l1, c) for l1, l2, c in overlaps]
    
    
    df2 = pd.DataFrame(overlaps, columns=['Loc1', 'Loc2', 'Count'])
    df2 = df2.set_index(['Loc1', 'Loc2'])
    df2 = df2.unstack().fillna(0).astype(int)
    
    
    #      Count
    # Loc2     A  B  C
    # Loc1
    # A        0  2  1
    # B        2  0  1
    # C        1  1  0
    

    如果您想忽略多重性,请将
    计数器(gp.Customer\u ID)
    替换为
    计数器(set(gp.Customer\u ID))

    我相信有一种更优雅的方法,但我现在提出了一个解决方案。基本上,您为每个客户建立一个邻接列表,然后相应地更新邻接矩阵:

    import pandas as pd
    
    #I'm assuming you can get your data into a pandas data frame:
    data = {'Customer_ID':[1,1,1,2,2],'Location':['A','B','C','A','B']}
    df = pd.DataFrame(data)
    
    #Initialize an empty matrix
    matrix_size = len(df.groupby('Location'))
    matrix = [[0 for col in range(matrix_size)] for row in range(matrix_size)]
    
    #To make life easier, I made a map to go from locations 
    #to row/col positions in the matrix
    location_set = list(set(df['Location'].tolist()))
    location_set.sort()
    location_map = dict(zip(location_set,range(len(location_set))))
    
    #Group data by customer, and create an adjacency list (dyct) for each
    #Update the matrix accordingly
    for name,group in df.groupby('Customer_ID'):
        locations = set(group['Location'].tolist())
        dyct = {}
        for i in locations:
            dyct[i] = list(locations.difference(i))
    
        #Loop through the adjacency list and update matrix
        for node, edges in dyct.items(): 
            for edge in edges:
                matrix[location_map[edge]][location_map[node]] +=1
    

    我相信有一种更优雅的方法,但我在飞行中想出了一个解决方案。基本上,你为每个客户建立一个邻接列表,然后相应地更新邻接矩阵:

    import pandas as pd
    
    #I'm assuming you can get your data into a pandas data frame:
    data = {'Customer_ID':[1,1,1,2,2],'Location':['A','B','C','A','B']}
    df = pd.DataFrame(data)
    
    #Initialize an empty matrix
    matrix_size = len(df.groupby('Location'))
    matrix = [[0 for col in range(matrix_size)] for row in range(matrix_size)]
    
    #To make life easier, I made a map to go from locations 
    #to row/col positions in the matrix
    location_set = list(set(df['Location'].tolist()))
    location_set.sort()
    location_map = dict(zip(location_set,range(len(location_set))))
    
    #Group data by customer, and create an adjacency list (dyct) for each
    #Update the matrix accordingly
    for name,group in df.groupby('Customer_ID'):
        locations = set(group['Location'].tolist())
        dyct = {}
        for i in locations:
            dyct[i] = list(locations.difference(i))
    
        #Loop through the adjacency list and update matrix
        for node, edges in dyct.items(): 
            for edge in edges:
                matrix[location_map[edge]][location_map[node]] +=1
    


    你能解释一下你的预期输出和/或输入吗?根据你提供的内容,邻接矩阵似乎没有意义。是的,我不知道你是如何定义这张图的。为什么位置之间的边,而不是客户和位置之间的边?为什么位置B与位置a之间有权重-2的边,而位置C没有?我想我理解了上一个矩阵中的一个错误。我现在已经更正了。希望它更有意义。好吧,我想我理解你的意图。对于每个客户,你基本上都在创建一个连通图。你要求的是表示这些图的邻接矩阵的总和。不清楚的是“a”、“B”和“C”是如何连通的。例如例如,客户“alpha”、位置“A”和“C”未连接。这是由于它们在数据中的显示顺序造成的吗?我们需要知道每个位置是如何连接的,以创建适当的邻接矩阵。图中的节点是
    位置
    s,每个
    位置
    都连接到所有其他位置,即边的权重(可能为零)是访问过这两个地方的
    客户的数量。Kurious,这种解释正确吗?你能解释你的预期输出和/或输入吗?根据你提供的内容,邻接矩阵似乎没有意义。是的,我不明白你是如何定义这个图的。为什么位置之间的边,而不是位置之间的边客户和位置?为什么位置B与位置C之间有权重-2的优势?我想我在上一个矩阵中犯了一个错误。我现在已经纠正了。希望它更有意义。好吧,我想我理解你的意图。对于每个客户,你基本上是在创建一个连通图。你要求的是一个权重-2的总和表示这些图的邻接矩阵。不清楚的是“A”、“B”和“C”如何连接。例如,客户“alpha”、位置“A”和“C”没有连接。这是因为它们在数据中出现的顺序吗?我们需要知道每个位置是如何连接的,以创建适当的邻接矩阵。图的节点是
    位置
    s,每个
    位置连接到所有其他位置,即边的权重(可能为零)是访问过这两个地方的
    客户的数量。Kurious,这个解释正确吗?这非常有帮助,非常全面,谢谢。顺便说一句,你能给我一些技巧,让我成熟地思考这些问题。我已经用Python编程近2年了(自学)。我不知道如何使用
    计数器
    产品
    。我在处理多个循环。我如何培养您所演示的那种熟练程度?现在这是一个问题…我当然远没有从总体上成熟地思考,但我发现以下一些资源很有用:stackoverflow显然非常有用很好;我喜欢认为我从回答像这样的问题中学到了很多。代码似乎有一些问题。如果两个地点之间没有普通客户(如示例矩阵中的C-D之间),我希望矩阵有一个0。但是,当前代码输入的是前往“行”位置的客户数。也许可以分享一个示例?我发现了一个可能导致它的错误。显然,计数器排序的行为与我的想法不同,因此我用明确的理解替换了它。我更新了问题中的示例。是否有其他信息我能提供的建议?这是非常有用和全面的,谢谢。顺便说一句,你能给我一些技巧,让我成熟地思考这些问题。我一直在用Pyth编程