我有一个非常大的数据框df
,如下所示:
ID Value1 Value2
1345 3.2 332
1355 2.2 32
2346 1.0 11
3456 8.9 322
我有一个包含ID ID_list
子集的列表。对于df
中包含的ID
,我需要有ID_list
的子集。
目前,我正在使用df_sub=df[df.ID.isin(ID_list)]
来执行此操作。但这需要很多时间。 ID
中包含的ID_list
没有任何模式,因此它不在一定范围内。 (我需要对许多类似的数据帧应用相同的操作。我想知道是否有更快的方法来执行此操作。如果将ID
作为索引,它会有很大帮助吗?
谢谢!
答案 0 :(得分:27)
编辑2:这里有一个指向各种pandas
操作性能的最新内容的链接,但它似乎并未包括合并和加入。
https://github.com/mm-mansour/Fast-Pandas
编辑1:这些基准测试是针对一个相当古老的熊猫版本,可能仍然没有相关性。请参阅以下关于merge
的Mike评论。
这取决于数据的大小,但对于大型数据集DataFrame.join似乎是要走的路。这需要您的DataFrame索引为您的ID'您加入的系列或DataFrame会有一个索引,即您的ID_list'。该系列还必须有一个name
与join
一起使用,并将其作为名为name
的新字段拉入。您还需要指定内部联接以获取类似isin
的内容,因为join
默认为左联接。对于大型数据集,查询in
语法似乎具有与isin
相同的速度特性。
如果你正在处理小数据集,你会得到不同的行为,使用列表理解或应用字典实际上比使用isin
更快。
否则,您可以尝试使用Cython来提高速度。
# I'm ignoring that the index is defaulting to a sequential number. You
# would need to explicitly assign your IDs to the index here, e.g.:
# >>> l_series.index = ID_list
mil = range(1000000)
l = mil
l_series = pd.Series(l)
df = pd.DataFrame(mil, columns=['ID'])
In [247]: %timeit df[df.index.isin(l)]
1 loops, best of 3: 1.12 s per loop
In [248]: %timeit df[df.index.isin(l_series)]
1 loops, best of 3: 549 ms per loop
# index vs column doesn't make a difference here
In [304]: %timeit df[df.ID.isin(l_series)]
1 loops, best of 3: 541 ms per loop
In [305]: %timeit df[df.index.isin(l_series)]
1 loops, best of 3: 529 ms per loop
# query 'in' syntax has the same performance as 'isin'
In [249]: %timeit df.query('index in @l')
1 loops, best of 3: 1.14 s per loop
In [250]: %timeit df.query('index in @l_series')
1 loops, best of 3: 564 ms per loop
# ID must be the index for DataFrame.join and l_series must have a name.
# join defaults to a left join so we need to specify inner for existence.
In [251]: %timeit df.join(l_series, how='inner')
10 loops, best of 3: 93.3 ms per loop
# Smaller datasets.
df = pd.DataFrame([1,2,3,4], columns=['ID'])
l = range(10000)
l_dict = dict(zip(l, l))
l_series = pd.Series(l)
l_series.name = 'ID_list'
In [363]: %timeit df.join(l_series, how='inner')
1000 loops, best of 3: 733 µs per loop
In [291]: %timeit df[df.ID.isin(l_dict)]
1000 loops, best of 3: 742 µs per loop
In [292]: %timeit df[df.ID.isin(l)]
1000 loops, best of 3: 771 µs per loop
In [294]: %timeit df[df.ID.isin(l_series)]
100 loops, best of 3: 2 ms per loop
# It's actually faster to use apply or a list comprehension for these small cases.
In [296]: %timeit df[[x in l_dict for x in df.ID]]
1000 loops, best of 3: 203 µs per loop
In [299]: %timeit df[df.ID.apply(lambda x: x in l_dict)]
1000 loops, best of 3: 297 µs per loop
答案 1 :(得分:1)
是的,isin
很慢。
相反,将ID
用作索引然后使用use loc
更快,例如:
df.set_index('ID', inplace=True)
df.loc[list_of_indices]
实际上,带我到此页面的是,我需要基于另一个df中的索引在df
中创建标签:“如果df_1的索引与df_2的索引匹配,则将其标记为1,否则将其标记为NaN”,我是这样完成的:
df_2['label'] = 1 # Create a label column
df_1.join(df_2['label'])
速度也很快。