从另一个Pandas DataFrame引用一行

时间:2017-06-16 01:16:15

标签: python pandas dataframe

假设我有2个DataFrame:

DataFrame 1

A  B
a  1
b  2
c  3
d  4

DataFrame2:

C D
a c 
b a
a b 

目标是向DataFrame 2添加一列(' E')。

C D E
a c (1-3=-2)
b a (2-1=1)
a b (1-2=-1)

如果这是excel,则公式可能类似于" = vlookup(A1,DataFrame1,2)-vlookup(B1,DataFrame1,2)"。知道这个公式在Python中是什么样的吗?

谢谢!

4 个答案:

答案 0 :(得分:3)

Pandas系列可以被认为是从其索引到其值的映射。 在这里,我们希望使用第一个DataFrame df1作为从列A到列B的映射。因此,自然要做的是将df1转换为系列:

s = df1.set_index('A')['B']
# A
# a    0
# b    1
# c    2
# d    3
# Name: B, dtype: int64

现在,我们可以使用Series.map方法根据s“查找”系列中的值:

import pandas as pd
df1 = pd.DataFrame({'A':list('abcd'), 'B':[1,2,3,4]})
df2 = pd.DataFrame({'C':list('aba'), 'D':list('cab')})
s = df1.set_index('A')['B']
df2['E'] = df2['C'].map(s) - df2['D'].map(s)

print(df2)

产量

   C  D  E
0  a  c -2
1  b  a  1
2  a  b -1

答案 1 :(得分:1)

您可以这样做:

#set column A as index, so you can index it
df1 = df1.set_index('A')

df2['E'] = df1.loc[df2.C, 'B'].values - df1.loc[df2.D, 'B'].values

结果是:

   C  D  E
0  a  c -2
1  b  a  1
2  a  b -1

希望有所帮助:)

答案 2 :(得分:1)

选项1
replaceevalassign

一起使用
df2.assign(E=df2.replace(df1.A.values, df1.B).eval('C - D'))

   C  D  E
0  a  c -2
1  b  a  1
2  a  b -1

我喜欢这个答案,因为它简洁明了。

  • 我使用replace和两个iterables,nameley df1.A指定要替换的内容,df1.B指定要替换的内容。
  • 我使用eval优雅地执行新发现C减去D的差异。
  • 我使用assign创建df2的副本,其中包含一个名为E的新列,其中包含上述步骤中的值。

我本可以使用字典而不是dict(zip(df1.A, df1.B))

df2.assign(E=df2.replace(dict(zip(df1.A, df1.B))).eval('C - D'))

   C  D  E
0  a  c -2
1  b  a  1
2  a  b -1

<强> PROJECT /
numpy + pd.factorize

base = df1.A.values
vals = df1.B.values
refs = df2.values.ravel()

f, u = pd.factorize(np.append(base, refs))
look = vals[f[base.size:]]
df2.assign(E=look[::2] - look[1::2])

   C  D  E
0  a  c -2
1  b  a  1
2  a  b -1

<强>时序
在纯pandas @ unutbu的答案中,明显的胜利者。虽然我过于冗长的numpy解决方案只能提高约40%

让我们将这些功能用于numpy版本。注意using_F_order是@ unutbu的贡献。

def using_numpy(df1, df2):
    base = df1.A.values
    vals = df1.B.values
    refs = df2.values.ravel()
    f, u = pd.factorize(np.append(base, refs))
    look = vals[f[base.size:]]
    return df2.assign(E=look[::2] - look[1::2])

def using_F_order(df1, df2):
    base = df1.A.values
    vals = df1.B.values
    refs = df2.values.ravel(order='F')
    f, u = pd.factorize(np.append(base, refs))
    look = vals[f[base.size:]].reshape(-1, 2, order='F')
    return df2.assign(E=look[:, 0]-look[:, 1])

小数据

%timeit df2.assign(E=df2.replace(df1.A.values, df1.B).eval('C - D'))
%timeit df2.assign(E=df2.replace(dict(zip(df1.A, df1.B))).eval('C - D'))
%timeit df2.assign(E=(lambda s: df2['C'].map(s) - df2['D'].map(s))(df1.set_index('A')['B']))
%timeit using_numpy(df1, df2)
%timeit using_F_order(df1, df2)

100 loops, best of 3: 2.31 ms per loop
100 loops, best of 3: 2.44 ms per loop
1000 loops, best of 3: 1.25 ms per loop
1000 loops, best of 3: 436 µs per loop
1000 loops, best of 3: 424 µs per loop

大数据

from string import ascii_lowercase, ascii_uppercase
import pandas as pd
import numpy as np

upper = np.array(list(ascii_uppercase))
lower = np.array(list(ascii_lowercase))

ch = np.core.defchararray.add(upper[:, None], lower).ravel()

np.random.seed([3,1415])
n = 100000
df1 = pd.DataFrame(dict(A=ch, B=np.arange(ch.size)))
df2 = pd.DataFrame(dict(C=np.random.choice(ch, n), D=np.random.choice(ch, n)))

%timeit df2.assign(E=df2.replace(df1.A.values, df1.B).eval('C - D'))
%timeit df2.assign(E=df2.replace(dict(zip(df1.A, df1.B))).eval('C - D'))
%timeit df2.assign(E=(lambda s: df2['C'].map(s) - df2['D'].map(s))(df1.set_index('A')['B']))
%timeit using_numpy(df1, df2)
%timeit using_F_order(df1, df2)

1 loop, best of 3: 11.1 s per loop
1 loop, best of 3: 10.6 s per loop
100 loops, best of 3: 17.7 ms per loop
100 loops, best of 3: 10.9 ms per loop
100 loops, best of 3: 9.11 ms per loop

答案 3 :(得分:0)

这是实现这一目标的一种非常简单的方法:

newdf = df2.replace(['a','b','c','d'],[1,2,3,4])
df2['E'] = newdf['C'] - newdf['D']
df2

我希望这有帮助!