【问题标题】:an algorithm for determining a consistent coding between two series of codes用于确定两个系列代码之间的一致编码的算法
【发布时间】:2020-05-16 14:21:02
【问题描述】:

我有一个问题,我试图使用人行横道来匹配数据集中的代码 A 和代码 B。例如,这些可能是 1990 年和 1991 年的行业代码,当时人口普查改变了他们对行业进行编码的方式。如果我可以创建一个统一的代码,我可以使用这个代码来跟踪相同的行业(或新的行业组,如有必要)。它们提供如下所示的人行横道:

import pandas as pd
import numpy as np

df = pd.DataFrame([
[0,0],
[1,2],
[1,3],
[2,4],
[3,4],
[4,5],
[4,6],
[5,5],
[10,11],
[10,13],
[11,11]
], columns=list('AB'))

df
     A   B
0    0   0
1    1   2
2    1   3
3    2   4
4    3   4
5    4   5
6    4   6
7    5   5
8   10  11
9   10  13
10  11  11

所以我想要的输出将是一个新列,它定义了A 和B 中的非重叠代码。例如,考虑下面的预期结果:

     A   B    C
0    0   0  0.0
1    1   2  1.0
2    1   3  1.0
3    2   4  2.0
4    3   4  2.0
5    4   5  3.0
6    4   6  3.0
7    5   5  3.0
8   10  11  4.0
9   10  13  4.0
10  11  11  4.0

我已经开始通过先完成简单的部分来回答这个问题。这些是(一对多)1:m 和(多对一)m:1 匹配项,我可以简单地分配一个公共值。

sizesA = df.groupby('A').size()
sizesB = df.groupby('B').size()
df['sizeA'] = df['A'].map(sizesA)
df['sizeB'] = df['B'].map(sizesB)

df['C'] = np.nan

next_v = 0

# 1:m matching

for a in df[df.sizeA>=1].A.unique():
    if df[df.A==a]['sizeB'].max()==1:
        df['C'] = np.where(df['A']==a, next_v, df['C'])
        next_v += 1

# m:1 matching

for b in df[df.sizeB>1].B.unique():
    if df[df.B==b]['sizeA'].max()==1:
        df['C'] = np.where(df['B']==b, next_v, df['C'])
        next_v += 1

df
     A   B  sizeA  sizeB    C
0    0   0      1      1  0.0
1    1   2      2      1  1.0
2    1   3      2      1  1.0
3    2   4      1      2  2.0
4    3   4      1      2  2.0
5    4   5      2      2  NaN
6    4   6      2      1  NaN
7    5   5      1      2  NaN
8   10  11      2      2  NaN
9   10  13      2      1  NaN
10  11  11      1      2  NaN

问题是(多对多)m:m 匹配。我似乎想不出解决这个问题的好方法,我猜这是一个计算上难以解决的问题。请注意,我可以只分配单个代码上方的剩余值,但这会忽略我可以将它们分成两个不同的代码并仍然保持一致性的事实。

另外,如果您对标题有任何建议,请告诉我。

【问题讨论】:

    标签: python pandas algorithm cluster-analysis data-science


    【解决方案1】:

    C 需要从 0 开始还是可以从 1 开始?我只是减去了 1,因此您可以从 0 开始。您可以根据需要对其进行调整。

    使用 cumcount 和 cumsum 应该会更容易。

    df['C'] = ((df.groupby('A').cumcount()==0) & (df.groupby('B').cumcount()==0)).cumsum()-1
    
    print(df)
    
    print(df)
    
         A   B  C
    0    0   0  0
    1    1   2  1
    2    1   3  1
    3    2   4  2
    4    3   4  2
    5    4   5  3
    6    4   6  3
    7    5   5  3
    8   10  11  4
    9   10  13  4
    10  11  11  4
    
    

    【讨论】:

    • 当我在更大的实际人行横道上运行它时,这似乎对我不起作用。我需要一些时间来给出一个更好的理由来说明为什么/在什么情况下它会失败。但无论如何,我想我找到了一个可行的算法。我也会发布那个。但是,如果单线工作,那就太好了。
    • 别担心,我很高兴您找到了可行的解决方案。我很好奇,它跑到哪里去了,我想知道我是否误解了要求。
    【解决方案2】:

    我实际上想出了一个解决方案。不过可能没那么优雅。

    ungrouped = df['A'].unique().tolist()
    Anew = pd.Series(index=sorted(df['A'].unique()))
    Bnew = pd.Series(index=sorted(df['B'].unique()))
    
    g = 0
    
    Avals = [ungrouped[0]]
    
    while True:
        Bvals = df[df['A'].isin(Avals)].B.unique().tolist()
    
        Acheck = df[df['B'].isin(Bvals)].A.unique().tolist()
    
        if set(Acheck) == set(Avals):
            Anew.loc[Avals] = g
            Bnew.loc[Bvals] = g
            g += 1
            ungrouped = [a for a in ungrouped if a not in Avals]
            if len(ungrouped) == 0:
                break
            Avals = [ungrouped[0]]
        else:
            Avals = Acheck
    
    df['C'] = df['A'].map(Anew)
    df
         A   B    C
    0    0   0  0.0
    1    1   2  1.0
    2    1   3  1.0
    3    2   4  2.0
    4    3   4  2.0
    5    4   5  3.0
    6    4   6  3.0
    7    5   5  3.0
    8   10  11  4.0
    9   10  13  4.0
    10  11  11  4.0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-26
      • 2022-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多