Python 在数据帧中高效地查找匹配行(基于内容)

Python 在数据帧中高效地查找匹配行(基于内容),python,pandas,dataframe,Python,Pandas,Dataframe,我正在编写一些测试,我正在使用Pandas数据帧来存储一个大数据集(600000 x 10)。我已经从源数据中提取了10个随机行(使用Stata),现在我想编写一个测试,看看这些行是否在我的测试套件的数据帧中 举个小例子 np.random.seed(2) raw_data = pd.DataFrame(np.random.rand(5,3), columns=['one', 'two', 'three']) random_sample = raw_data.ix[1] 这里的原始数据是:

我正在编写一些测试,我正在使用Pandas数据帧来存储一个大数据集(600000 x 10)。我已经从源数据中提取了10个随机行(使用Stata),现在我想编写一个测试,看看这些行是否在我的测试套件的数据帧中

举个小例子

np.random.seed(2)
raw_data = pd.DataFrame(np.random.rand(5,3), columns=['one', 'two', 'three'])
random_sample = raw_data.ix[1]
这里的原始数据是:

random_sample
是为保证匹配而派生的,是:

目前我写了:

for idx, row in raw_data.iterrows():
    if random_sample.equals(row):
        print "match"
        break
但在大型数据集上运行速度非常慢。是否有更有效的方法来检查数据帧中是否包含整行


顺便说一句:我的例子还需要能够比较
np.NaN
相等,这就是为什么我使用
equals()
方法的原因。我想到的最好的方法是采用一种过滤方法,这种方法似乎工作得很好,并且在数据集很大时可以防止大量比较:

tmp = raw_data    
for idx, val in random_sample.iteritems():
    try:
        if np.isnan(val):
            continue
    except:
        pass
    tmp = tmp[tmp[idx] == val]
if len(tmp) == 1: print "match"

注意:对于上面的小尺寸示例,这实际上是一个较慢的过程。但在大数据集上,这比基本迭代快约9倍,我想到的最好方法是采用一种过滤方法,这种方法似乎工作得很好,并且在数据集很大时可以防止大量比较:

tmp = raw_data    
for idx, val in random_sample.iteritems():
    try:
        if np.isnan(val):
            continue
    except:
        pass
    tmp = tmp[tmp[idx] == val]
if len(tmp) == 1: print "match"

注意:对于上面的小尺寸示例,这实际上是一个较慢的过程。但是在一个大数据集上,这比基本迭代快约9倍

equals
似乎没有广播,但我们始终可以手动进行等式比较:

>>> df = pd.DataFrame(np.random.rand(600000, 10))
>>> sample = df.iloc[-1]
>>> %timeit df[((df == sample) | (df.isnull() & sample.isnull())).all(1)]
1 loops, best of 3: 231 ms per loop
>>> df[((df == sample) | (df.isnull() & sample.isnull())).all(1)]
              0         1         2         3         4         5         6  \
599999  0.07832  0.064828  0.502513  0.851816  0.976464  0.761231  0.275242   

               7        8         9  
599999  0.426393  0.91632  0.569807  
对我来说,这比迭代版本快得多(需要>30秒)

但是,由于我们有很多行和相对较少的列,我们可以在列上循环,在典型的情况下,可能会大大减少要查看的行数。比如说

def finder(df, row):
    for col in df:
        df = df.loc[(df[col] == row[col]) | (df[col].isnull() & pd.isnull(row[col]))]
    return df
给我

>>> %timeit finder(df, sample)
10 loops, best of 3: 35.2 ms per loop
这大约快了一个数量级,因为在第一列之后只剩下一行


(我想我曾经有一个更巧妙的方法来实现这一点,但就我的一生而言,我现在记不起来了。)

equals
似乎没有广播,但我们总是可以手动进行相等比较:

>>> df = pd.DataFrame(np.random.rand(600000, 10))
>>> sample = df.iloc[-1]
>>> %timeit df[((df == sample) | (df.isnull() & sample.isnull())).all(1)]
1 loops, best of 3: 231 ms per loop
>>> df[((df == sample) | (df.isnull() & sample.isnull())).all(1)]
              0         1         2         3         4         5         6  \
599999  0.07832  0.064828  0.502513  0.851816  0.976464  0.761231  0.275242   

               7        8         9  
599999  0.426393  0.91632  0.569807  
对我来说,这比迭代版本快得多(需要>30秒)

但是,由于我们有很多行和相对较少的列,我们可以在列上循环,在典型的情况下,可能会大大减少要查看的行数。比如说

def finder(df, row):
    for col in df:
        df = df.loc[(df[col] == row[col]) | (df[col].isnull() & pd.isnull(row[col]))]
    return df
给我

>>> %timeit finder(df, sample)
10 loops, best of 3: 35.2 ms per loop
这大约快了一个数量级,因为在第一列之后只剩下一行


(我想我曾经有过一种更巧妙的方法来实现这一点,但就我的一生而言,我现在记不得了。)

finder列迭代是最快的。根据我的数据,我在测试许多随机样本时,每个循环得到约1.63s。我的过滤方法是每循环1.72秒。简单广播以每秒4.14秒的速度播放loop@sanguineturtle:如果您正在进行多个搜索,您可以尝试使用
isin
对帧进行剔除或排序,然后在其中搜索。(不过,如果只进行少量搜索,排序的成本可能太高。)我重新查看了这段代码,发现了一个关于上面finder()函数的小错误。虽然它检查每个值,但实际上返回一个“匹配”行,该行仅基于与最后一列值的匹配?@sanguineturtle:我不确定你的意思。如果更改要搜索的行值,使其中一个不匹配,
finder
将返回一个空帧。finder列迭代最快。根据我的数据,我在测试许多随机样本时,每个循环得到约1.63s。我的过滤方法是每循环1.72秒。简单广播以每秒4.14秒的速度播放loop@sanguineturtle:如果您正在进行多个搜索,您可以尝试使用
isin
对帧进行剔除或排序,然后在其中搜索。(不过,如果只进行少量搜索,排序的成本可能太高。)我重新查看了这段代码,发现了一个关于上面finder()函数的小错误。虽然它检查每个值,但实际上返回一个“匹配”行,该行仅基于与最后一列值的匹配?@sanguineturtle:我不确定你的意思。如果更改要搜索的行值,使其中一个不匹配,
finder
将返回空帧。