【问题标题】:(pandas)How can I create a unique identifier based on three similar columns of data, where order doesn't matter?(熊猫)如何根据三个相似的数据列创建唯一标识符,其中顺序无关紧要?
【发布时间】:2019-10-31 04:27:28
【问题描述】:

(Python/熊猫) 我正在对英国房价数据进行一些分析,看看房价是否对附近学校的质量有反应。我已将最近的三所学校的 URN(唯一参考编号)与每个房价交易匹配。这些是数据中的 URN_1、URN_2、URN_3 列。

我想根据数据估计一个固定效应模型,其中固定效应基于最近的三所学校。因此,我想为三所学校的每个集群创建一个唯一 ID,并且我希望这不受学校顺序的影响。物业 A 和物业 B 应具有相同的 ID,尽管学校的顺序不同。

Property    URN_1   URN_2   URN_3
A         100053   100052   100054
B         100052   100054   100053

有谁知道我如何使用 Python 创建唯一的集群 ID?

我尝试使用 .groupby() 来使用下面的代码创建 ID,但是当学校的顺序不同时,这会给出不同的集群 ID。

这是我尝试过的:

import pandas as pd
URN1=[1,2,3,4,5]
URN2=[5,4,3,2,1]
URN3=[1,2,3,2,1]
lst=['a','b','c','d','e']
df=pd.DataFrame(list(zip(URN1,URN2,URN3)),
columns['URN_1','URN_2','URN_3'],index=lst)
df['clusterid']=df.groupby(['URN_1','URN_2','URN_3']).ngroup()
print(df)

我希望观察结果 'a' 和 'e' 具有相同的集群 id,但是通过这种方法它们被赋予了不同的 id。

【问题讨论】:

    标签: python pandas uniqueidentifier


    【解决方案1】:

    在组合的唯一类字符串对象上使用factorize。由于顺序无所谓,我们先排序再合并。

    df['clusterid'] = pd.factorize(df[['URN_1','URN_2','URN_3']].apply(lambda x: ','.join([str(y) for y in sorted(x)]),1))[0]
    

    输出:

           URN_1  URN_2  URN_3  clusterid  clisterid
    a      1      5      1          0          0
    b      2      4      2          1          1
    c      3      3      3          2          2
    d      4      2      2          3          1
    e      5      1      1          4          0
    

    【讨论】:

    • 谢谢 harvpan。这对这个练习数据很有用并且非常有效,但是在实际数据中有一百万个数据点和许多相邻的 URN,所以我担心它会错误地将观察分组,其中 URN 恰好总和相同的东西.例如如果您添加观察 'f'=[7,0,0] 您的方法将错误地将其与 'a' 和 'e' 分组
    • @EduinJBL,请查看编辑。这应该可以解决问题。
    【解决方案2】:

    如果您的数据不太长,此方法有效:

    # we sort the values of each row
    # and turn them to tuples
    markers = (df[['URN_1','URN_2','URN_3']]
                 .apply(lambda x: tuple(sorted(x.values)), axis=1)
              )
    
    df['clisterid'] = df.groupby(markers).ngroup()
    

    输出:

      Property   URN_1   URN_2   URN_3  clisterid
    0        A  100053  100052  100054          0
    1        B  100052  100054  100053          0
    

    选项 2: 因为上述解决方案使用 apply,在某些情况下可能并不理想。这是一个小数学技巧:众所周知,(a,b,c) 组是由(a+b+c, a**2+b**2+c**2, abc) 唯一定义的(直到排列)。所以我们可以计算这些值并按它们分组:

    tmp_df = df[['URN_1','URN_2','URN_3']]
    
    s = tmp_df.sum(1)         # sums
    sq = (tmp_df**2).sum(1)   # sum of squares
    p = tmp_df.prod(1)        # products
    
    # groupby
    df['clisterid'] = df.groupby([s,sq,p]).ngroup()
    

    性能:第一种方法需要 14 秒来处理 200 万行,而第二种方法需要不到 1 秒。

    【讨论】:

    • 谢谢Quang,明天我加载数据时会试试这个。我有 200 万个观察值,你认为这会使这个方法太慢吗?
    • @EduinJBL 尝试第二种方法,在我的计算机上花费不到 1 秒的时间处理 200 万行。
    • @QuangHoang,您如何评估 2M 行所需的时间?
    • 我创建了一个数据框df = pd.DataFrame(np.random.randint(0,100, (2000000,3)), columns=['URN_1','URN_2','URN_3'])并使用%%time
    • 感谢您的测试。 .apply 确实很慢。 +1
    【解决方案3】:

    您可以使用已排序的 3 个 URN 为每个创建一个字符串。
    然后按这个新变量分组并像之前尝试过的那样使用 ngroup()

    df['URN_join'] = df[['URN_1','URN_2','URN_3']].apply(lambda x: '_'.join([str(nb) for nb in sorted(x)]), axis=1)
    df['clusterid'] = df.groupby(['URN_join']).ngroup()
    df
    

    输出:

        URN_1   URN_2   URN_3   clusterid   URN_join
    a   1       5       1       0           1_1_5
    b   2       4       2       1           2_2_4
    c   3       3       3       2           3_3_3
    d   4       2       2       1           2_2_4
    e   5       1       1       0           1_1_5
    

    【讨论】:

    • 谢谢,明天我重新加载数据时试试这个。
    猜你喜欢
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多