【问题标题】:series.unique vs list of set - performanceseries.unique vs 集合列表 - 性能
【发布时间】:2017-10-19 21:36:25
【问题描述】:

我有一个包含多列的 pandas 数据框。目的是在其中一列中找到唯一值。

实现这一点的两种方法是:

  • 获取该系列的集合列表:list(set(data['Day']))

  • 使用 pandas 的函数data['Day'].unique()获取唯一性@

在我的试验中,set 方法运行得更快。大多数情况下都是这样吗?为什么和为什么不?任何其他资源利用影响?

还请说明为什么它们中的任何一个效果更好。

【问题讨论】:

  • 你是如何测试这个的?
  • 哪个 dtype 有Day 列?
  • 我使用了一个 CSV 文件,该文件有大约 250 万行,在我试图获取唯一值的列中有 15 个唯一值。列的数据类型是字符串。

标签: python pandas unique


【解决方案1】:

这取决于数据类型。对于数字类型,pd.unique 应该快得多。

对于存储为 python 对象的字符串,差异会小得多,set() 通常会具有竞争力,因为它正在做非常相似的事情。

一些例子:

strs = np.repeat(np.array(['a', 'b', 'c'], dtype='O'), 10000)

In [11]: %timeit pd.unique(strs)
558 µs ± 16.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [12]: %timeit list(set(strs))
531 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

nums = np.repeat(np.array([1, 2, 3]), 10000)

In [13]: %timeit pd.unique(nums)
230 µs ± 9.28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [14]: %timeit list(set(nums))
2.16 ms ± 71 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【讨论】:

  • 您能否补充说明为什么set 方法在字符串上更快的原因?即使名义上。谢谢!
  • 27 µs 并不是一个显着差异。这两种方法都在遍历 python 对象,构造一个哈希表,然后根据该表提取一个唯一的列表。差异可能是由于不同点的相对开销造成的。
【解决方案2】:

对具有很少唯一值的列使用分类 dtype 是有意义的。

演示:

 df = pd.DataFrame(np.random.choice(['aa','bbbb','c','ddddd','EeeeE','xxx'], 10**6), columns=['Day'])

In [34]: %timeit list(set(df['Day']))
98.1 ms ± 2.96 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [35]: %timeit df['Day'].unique()
82.9 ms ± 56.5 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

1M 行的时间几乎相同

让我们测试类别 dtype:

In [37]: df['cat'] = df['Day'].astype('category')

In [38]: %timeit list(set(df['cat']))
93.7 ms ± 766 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [39]: %timeit df['cat'].unique()
25.1 ms ± 6.57 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

更新: 1.000.000 行中的 500 个唯一值 DF:

In [75]: a = pd.util.testing.rands_array(10, 500)

In [76]: df = pd.DataFrame({'Day':np.random.choice(a, 10**6)})

In [77]: df.shape
Out[77]: (1000000, 1)

In [78]: df.Day.nunique()
Out[78]: 500

In [79]: %timeit list(set(df['Day']))
55 ms ± 395 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [80]: %timeit df['Day'].unique()
133 ms ± 3.34 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [81]: df['cat'] = df['Day'].astype('category')

In [82]: %timeit list(set(df['cat']))
102 ms ± 3.64 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [83]: %timeit df['cat'].unique()
38.3 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

结论:对你的真实数据“计时”总是更好的——你可能会得到不同的结果......

【讨论】:

  • 有趣。尽管它提出了关于“少数独特价值”的好数字是多少的问题。虽然在我的情况下它会超过 100,但它对某些情况很有帮助。
  • @Wen,我测试了%timeit np.unique(df['Day'].values)[0] - 它花了2.51 s
  • 嘿@MaxU,我明白了。对于这些行为为什么会发生,你有核心原因吗?此外,由于对类别的转换仅对 .unique 函数产生了促进作用,因此您应该使用转换和系列集合的唯一与普通列表的组合来计时。有意义吗?
【解决方案3】:

结果似乎在唯一条目的数量上差异很大:

这是对 ipl 数据集(交付)的一些时间测试

对于具有 577 个唯一 id 的列 match_id:unique() 似乎非常有效

%timeit list(set(deliveries['match_id']))
27.5 ms ± 2.09 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit deliveries['match_id'].unique()
1.79 ms ± 322 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

对于有 13 个独特球队的 batting_team 栏:list(set()) 稍微好一点

%timeit list(set(deliveries['batting_team']))
9.92 ms ± 945 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit deliveries['batting_team'].unique()
10.2 ms ± 315 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

编辑:试图测试弦理论。在具有 436 个唯一条目的字符串列 batsman 上运行相同的测试

%timeit list(set(deliveries['batsman']))
9.32 ms ± 431 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit deliveries['batsman'].unique()
8.06 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【讨论】:

  • 有趣。你知道这种行为背后的原因吗?如果是这样,您能否将其添加到答案中?谢谢。
猜你喜欢
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 2011-12-23
  • 1970-01-01
相关资源
最近更新 更多