pandas Python中R的match()用于索引的等价性

g52tjvyc  于 2023-04-18  发布在  Python
关注(0)|答案(4)|浏览(191)

所以我本质上想在Python中实现R的match()函数的等价物,使用Pandas Dataframe -而不使用for循环。
在R中,match()返回第一个参数的(第一个)匹配在第二个参数中的位置的向量。
假设我有两个df A和B,它们都包括列C。其中

A$C = c('a','b')
B$C = c('c','c','b','b','c','b','a','a')

在R中我们会得到

match(A$C,B$C) = c(7,3)

Python中对于pandas数据框中的列有一个等效的方法,它不需要循环遍历值。

ebdffaop

ebdffaop1#

下面是一个 * 一行 *:
B.reset_index().groupby('C')['index'].first()[A.C].values
这个解决方案以与输入A相同的顺序返回结果,就像match在R中所做的那样。

完整示例:

import pandas as pd

A = pd.DataFrame({'C':['a','b']})
B = pd.DataFrame({'C':['c','c','b','b','c','b','a','a']})

B.reset_index().groupby('C')['index'].first()[A.C].values
Output array([6, 2])

***编辑(2023-04-12):***在pandas的新版本中,.loc匹配所有符合条件的行。因此,以前的解决方案(B.reset_index().set_index('c').loc[A.c, 'index'].values)将返回所有匹配,而不仅仅是第一个匹配。

3qpi33ja

3qpi33ja2#

您可以先使用drop_duplicates,然后使用boolean indexingisinmerge
Python从0开始计数,因此对于相同的输出添加1

A = pd.DataFrame({'c':['a','b']})
B = pd.DataFrame({'c':['c','c','b','b','c','b','a','a']})

B = B.drop_duplicates('c')
print (B)
   c
0  c
2  b
6  a

print (B[B.c.isin(A.c)])
   c
2  b
6  a

print (B[B.c.isin(A.c)].index)
Int64Index([2, 6], dtype='int64')
print (pd.merge(B.reset_index(), A))
   index  c
0      2  b
1      6  a

print (pd.merge(B.reset_index(), A)['index'])
0    2
1    6
Name: index, dtype: int64
4urapxun

4urapxun3#

这给出了所有匹配的索引(使用python的基于0的索引):

import pandas as pd

df1 = pd.DataFrame({'C': ['a','b']})
print df1

   C
0  a
1  b

df2 = pd.DataFrame({'C': ['c','c','b','b','c','b','a','a']})
print df2   

   C
0  c
1  c
2  b
3  b
4  c
5  b
6  a
7  a

match = df2['C'].isin(df1['C'])
print [i for i in range(match.shape[0]) if match[i]]

#[2, 3, 5, 6, 7]
q43xntqr

q43xntqr4#

下面是一个包含各种方法的实现。如果不使用C或C++,最快的方法似乎是datatable

def match(x, y, method = "dt"):
  '''
  x and y are two numpy 1d arrays containing only finite values.  
  
  method = 'dt': use datatable
  method = 'pandas': use pandas
  method = 'numpy': use numpy
  method = 'dict': use hashing.
  '''
  if method == 'dt': # Use datatable
    xdf = datatable.Frame({'val': x})
    ydf = datatable.Frame({'val': y, 'ind': np.arange(y.shape[0]) })[
      :, datatable.min(datatable.f.ind), datatable.by(datatable.f.val)]
    ydf.key = 'val'
    rst = xdf[:, :, datatable.join(ydf)]['ind'].to_numpy()
    return rst.filled(-1 - y.shape[0]).ravel()
  
  
  if method == 'pandas': # Use pandas dataframe.
    xdf = pd.DataFrame({'val': x})
    ydf = pd.DataFrame({'val': y, 'ind': np.arange(y.shape[0]) }).groupby(
      ['val']).min()
    joined = xdf.join(ydf, on = 'val', lsuffix = '_x', rsuffix = '_y')
    rst = joined['ind'].to_numpy()
    rst[np.isnan(rst)] = -1 - y.shape[0]
    return rst.astype(int)
  
  
  rst = np.zeros(x.shape[0], dtype = np.int32) - (y.shape[0] + 1)
  if method == 'numpy':
    yorder = y.argsort()
    ysorted = y[yorder]
    ind = np.searchsorted(ysorted, x)
    outofBound = ind >= y.shape[0]
    ind[outofBound] = 0
    eq = ysorted[ind] == x
    eq[outofBound] = False
    rst[eq] = yorder[ind][eq]

  else: # Hashing.

    D = dict(zip(y[::-1], np.arange(y.shape[0] - 1, -1, -1)))
    for i, u in enumerate(x):
      val = D.get(u)
      if val is not None: rst[i] = val
  return rst

测试代码:

import datatable
  import pandas
  import time
  import numpy as np
  
  
  N = int(1e9)
  k = int(1e7)
  x = np.random.choice(N, k)
  y = np.random.choice(N, k)
  timeCosts = {}
  
  
  st = time.time()
  ind = match(x, y, "dt")
  timeCosts['datatable'] = time.time() - st
  np.all(x[ind >= 0] == y[ind[ind >= 0]])
  
  
  st = time.time()
  ind = match(x, y, "pandas")
  timeCosts['pandas'] = time.time() - st
  np.all(x[ind >= 0] == y[ind[ind >= 0]])
  
  
  st = time.time()
  ind = match(x, y, "numpy")
  timeCosts['numpy'] = time.time() - st
  np.all(x[ind >= 0] == y[ind[ind >= 0]])
  
  
  st = time.time()
  ind = match(x, y, "hashing")
  timeCosts['hashing'] = time.time() - st
  np.all(x[ind >= 0] == y[ind[ind >= 0]])

时间以秒为单位:{'datatable': 1.55, 'pandas': 8.01, 'numpy': 14.91, 'hashing': 6.04}
但最快的仍然比R的match慢:1.05s
R一定使用了一些类似于基数排序的哈希技术。

相关问题