为什么Pandas允许非唯一索引?

6
Pandas支持使用唯一和非唯一索引。某些操作只允许使用唯一索引。在什么情况下使用非唯一索引是有意义的呢?我认为强制索引的唯一性可以帮助及早发现数据完整性问题。

5
如果您执行类似于 df.explode 的操作,有必要保留非唯一的索引以指示这些行最初属于同一组。 - Henry Yik
2个回答

3
免责声明:唯一的 RangeIndex 始终是最高效的选项。本问题似乎更喜欢使用唯一索引,并且专门寻找允许非唯一索引的情况。因此,从现在开始,将不讨论唯一索引,也不考虑性能,仅讨论非唯一索引的有用好处。
通常情况下,非唯一索引更可取,无论何时我们需要跟踪数据的原始来源。在许多情况下,在中间阶段,我们需要知道数据所在的行。这使我们能够根据可能会丢失的信息进行计算,如果索引是唯一的,则需要添加额外的列来跟踪它。以下只是一些示例:

多个DataFrame交错:

考虑以下两个数据框,假设每个数据框代表一天的数据。我们想按样本编号而不是按天来查看这些每日数据:

df1 = pd.DataFrame([['10:05', 'Day 1', 'Sample 1'],
                    ['11:14', 'Day 1', 'Sample 2']])
df2 = pd.DataFrame([['10:03', 'Day 2', 'Sample 1'],
                    ['11:12', 'Day 1', 'Sample 2']])

# df1
       0      1         2
0  10:05  Day 1  Sample 1
1  11:14  Day 1  Sample 2

#df2
       0      1         2
0  10:03  Day 2  Sample 1
1  11:12  Day 1  Sample 2

由于pandas允许非唯一索引,因此我们可以 concat 然后 sort_index
pd.concat([df1, df2]).sort_index()

       0      1         2
0  10:05  Day 1  Sample 1
0  10:03  Day 2  Sample 1
1  11:14  Day 1  Sample 2
1  11:12  Day 1  Sample 2

请注意,这是按行索引交错两个数据帧的最快方法。还要注意,如果按列1和2进行排序,将按字典顺序排序单词“Day 1”、“Sample 1”等,这将在处理像“Day 10”这样的值时遇到问题,或者需要大量的额外计算来正确处理数字值。
我们可以添加ignore_index=True到sort_index中,但这只是隐藏了使用新范围索引覆盖的事实,并仍然依赖于concat返回具有非唯一索引的DataFrame的事实。
pd.concat([df1, df2]).sort_index(ignore_index=True)

       0      1         2
0  10:05  Day 1  Sample 1
1  10:03  Day 2  Sample 1
2  11:14  Day 1  Sample 2
3  11:12  Day 1  Sample 2

拆分和压缩

explode,尤其是在Series中,是一种常见操作,不会丢失索引(允许重复),这使得扩展和压缩类型的操作变得更加容易。

目标是从列中的逗号分隔字符串中删除任何重复值:

df = pd.DataFrame({
    'corresponding_id': [10, 20, 30],
    'col': ['a,b,c,a', 'b,c,c,b', 'a,a,a,a']
})

df:

   corresponding_id      col
0                10  a,b,c,a
1                20  b,c,c,b
2                30  a,a,a,a

一种常见的解决方案可能如下所示:
df['col'] = (
    df['col'].str.split(',').explode()
        .groupby(level=0).apply(lambda s: ','.join(np.unique(s)))
)

df:

   corresponding_id    col
0                10  a,b,c
1                20    b,c
2                30      a

爆炸后,结果看起来像这样:

df['col'].str.split(',').explode()

0    a
0    b
0    c
0    a
1    b
1    c
1    c
1    b
2    a
2    a
2    a
2    a
Name: col, dtype: object

由于存在重复的索引,我们可以相对于level=0(即索引)进行groupby,这只有在保留索引的情况下才可能。如果索引不允许重复,我们将会有:
0     a
1     b
2     c
3     a
4     b
5     c
6     c
7     b
8     a
9     a
10    a
11    a
Name: col, dtype: object

很难确定这些值来自哪些行,使得把它们放回原位变得更加困难。


对DataFrame进行扩展

使用重复标签从DataFrame中选择数据非常有帮助,可以帮助对DataFrame进行扩展。

df = pd.DataFrame({
    'Count': [2, 4],
    'Value': [1, 6]
})

有时我们需要扩展一个DataFrame,在这种情况下,我们使用 loc 从DataFrame中进行选择:
df.loc[[0, 0, 1, 1, 1, 1], :]

请注意,结果是:
   Count  Value
0      2      1
0      2      1
1      4      6
1      4      6
1      4      6
1      4      6

我们能够基于重复的标签从DataFrame中多次选择同一行(结果索引是非唯一的)。这是如此常见,以至于有一个方法Index.repeat可以根据列动态执行此操作:
df.loc[df.index.repeat(df['Count']), :]

   Count  Value
0      2      1
0      2      1
1      4      6
1      4      6
1      4      6
1      4      6

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接