2017-09-05 76 views
4

我有两个数据框,df和df2,它们是通讯组。 现在基于第一个数据框df,我想在一行中得到3个最小值,并返回对应列的名称(在本例中为“X”或“Y”或“Z”或“T”)。所以我可以得到新的数据帧df3。获取每行三个最小值并返回相应的列名称

df = pd.DataFrame({ 
     'X': [21, 2, 43, 44, 56, 67, 7, 38, 29, 130], 
     'Y': [101, 220, 330, 140, 250, 10, 207, 320, 420, 50], 
     'Z': [20, 128, 136, 144, 312, 10, 82, 63, 42, 12], 
     'T': [2, 32, 4, 424, 256, 167, 27, 38, 229, 30] 
    }, index=list('ABCDEFGHIJ')) 

df2 = pd.DataFrame({ 
     'X': [0.5, 0.12,0.43, 0.424, 0.65,0.867,0.17,0.938,0.229,0.113], 
     'Y': [0.1,2.201,0.33,0.140,0.525,0.31,0.20,0.32,0.420,0.650], 
     'Z': [0.20,0.128,0.136,0.2144,0.5312,0.61,0.82,0.363,0.542,0.512], 
     'T':[0.52, 0.232,0.34, 0.6424, 0.6256,0.3167,0.527,0.38,0.4229,0.73] 
    },index=list('ABCDEFGHIJ')) 

除此之外,我想另一个数据帧DF4这是记者从DF3 DF2中,这意味着在DF行[“A”(2,20,21)是3最小值,所以在DF4行['A'],我想从df2获得(0.52,0.2,0.5)。

谢谢。

+0

什么是DF和DF2以及如何之间的关系是它涉及创建DF3? –

+0

df和df2不相关。在创建df3时,我们应该只使用df。 – Hong

+0

Numpy解决方案约。接受答案的速度提高10倍,在我的答案中查看时间。 – jezrael

回答

2

您可以使用,如果这两个DataFrames具有相同的顺序argsort相同的列名的索引:

arr = df.values.argsort(1)[:,:3] 
print (arr) 
[[0 3 1] 
[1 0 3] 
[0 1 3] 
[1 2 3] 
[1 2 0] 
[2 3 1] 
[1 0 3] 
[0 1 3] 
[1 3 0] 
[3 0 2]] 

#get values by indices in arr 
b = df2.values[np.arange(len(arr))[:,None], arr] 
print (b) 
[[ 0.52 0.2  0.5 ] 
[ 0.12 0.232 0.128 ] 
[ 0.34 0.43 0.136 ] 
[ 0.424 0.14 0.2144] 
[ 0.65 0.525 0.6256] 
[ 0.31 0.61 0.867 ] 
[ 0.17 0.527 0.82 ] 
[ 0.38 0.938 0.363 ] 
[ 0.229 0.542 0.4229] 
[ 0.512 0.73 0.65 ]] 

最后使用DataFrame构造函数:

df3 = pd.DataFrame(df.columns[arr]) 
df3.columns = ['Col{}'.format(x+1) for x in df3.columns] 
print (df3) 
    Col1 Col2 Col3 
0 T Z X 
1 X T Z 
2 T X Z 
3 X Y Z 
4 X Y T 
5 Y Z X 
6 X T Z 
7 T X Z 
8 X Z T 
9 Z T Y 

df4 = pd.DataFrame(b) 
df4.columns = ['Col{}'.format(x+1) for x in df4.columns] 
print (df4) 
    Col1 Col2 Col3 
0 0.520 0.200 0.5000 
1 0.120 0.232 0.1280 
2 0.340 0.430 0.1360 
3 0.424 0.140 0.2144 
4 0.650 0.525 0.6256 
5 0.310 0.610 0.8670 
6 0.170 0.527 0.8200 
7 0.380 0.938 0.3630 
8 0.229 0.542 0.4229 
9 0.512 0.730 0.6500 

答案是相似的,所以我创建了计时

np.random.seed(14) 
N = 1000000 
df1 = pd.DataFrame(np.random.randint(100, size=(N, 4)), columns=['X','Y','Z','T']) 
#print (df1) 

df1 = pd.DataFrame(np.random.rand(N, 4), columns=['X','Y','Z','T']) 
#print (df1) 


def jez(): 
    arr = df.values.argsort(1)[:,:3] 
    b = df2.values[np.arange(len(arr))[:,None], arr] 
    df3 = pd.DataFrame(df.columns[arr]) 
    df3.columns = ['Col{}'.format(x+1) for x in df3.columns] 
    df4 = pd.DataFrame(b) 
    df4.columns = ['Col{}'.format(x+1) for x in df4.columns] 


def pir(): 
    v = df.values 
    a = v.argpartition(3, 1)[:, :3] 
    c = df.columns.values[a] 
    pd.DataFrame(c, df.index) 
    d = df2.values[np.arange(len(df))[:, None], a] 
    pd.DataFrame(d, df.index, [1, 2, 3]).add_prefix('Col') 

def cᴏʟᴅsᴘᴇᴇᴅ(): 
    #another solution is wrong 
    df3 = df.apply(lambda x: df.columns[np.argsort(x)], 1).iloc[:, :3] 
    pd.DataFrame({'Col{}'.format(i + 1) : df2.lookup(df3.index, df3.iloc[:, i]) for i in range(df3.shape[1])}, index=df.index) 


print (jez()) 
print (pir()) 
print (cᴏʟᴅsᴘᴇᴇᴅ()) 

In [176]: %timeit (jez()) 
1000 loops, best of 3: 412 µs per loop 

In [177]: %timeit (pir()) 
1000 loops, best of 3: 425 µs per loop 

In [178]: %timeit (cᴏʟᴅsᴘᴇᴇᴅ()) 
100 loops, best of 3: 3.99 ms per loop 
+0

第二种解决方案是正确的。第一个解决方案不包含标题。它们是相同的。 –

+1

我尝试了df4的ger错误,我想只有一些错字,你能检查它吗? – jezrael

+0

是的。该指数已关闭。我的第二个解决方案是1.5ms。 –

2

您可以使用np.argsort来检索每一行中最小项目的列名称。

df3 = df.apply(lambda x: df.columns[np.argsort(x)], 1).iloc[:, :3] 
print(df3) 

A T Z X 
B X T Z 
C T X Z 
D X Y Z 
E X Y T 
F Y Z X 
G X T Z 
H T X Z 
I X Z T 
J Z T Y 

,这是让df3的改进涉及索引df.columns直接(通过jezrael's answer启发):

​​

使用df3,索引df2使用df.lookup

df4 = pd.DataFrame({'Col{}'.format(i + 1) : df2.lookup(df3.index, df3.iloc[:, i])\ 
             for i in range(df3.shape[1])}, index=df.index) 
print(df4) 

    Col1 Col2 Col3 
A 0.520 0.200 0.5000 
B 0.120 0.232 0.1280 
C 0.340 0.430 0.1360 
D 0.424 0.140 0.2144 
E 0.650 0.525 0.6256 
F 0.310 0.610 0.8670 
G 0.170 0.527 0.8200 
H 0.380 0.938 0.3630 
I 0.229 0.542 0.4229 
J 0.512 0.730 0.6500 

2

我会用numpy.argpartition,因为它只是看起来每一行划分成底部k和休息。由于不需要完全排序,其时间复杂度为O(n)而不是O(nlogn)。我们可以在此基础上定义df3

df3 = pd.DataFrame(c, df.index) 

df3 

    0 1 2 
A T Z X 
B X T Z 
C T X Z 
D Y X Z 
E Y X T 
F Y Z X 
G X T Z 
H X T Z 
I X Z T 
J Z T Y 

你可以用它来创造df4

d = df2.values[np.arange(len(df))[:, None], a] 
df4 = pd.DataFrame(d, df.index, [1, 2, 3]).add_prefix('Col') 
df4 

    Col1 Col2 Col3 
A 0.520 0.200 0.5000 
B 0.120 0.232 0.1280 
C 0.340 0.430 0.1360 
D 0.140 0.424 0.2144 
E 0.525 0.650 0.6256 
F 0.310 0.610 0.8670 
G 0.170 0.527 0.8200 
H 0.938 0.380 0.3630 
I 0.229 0.542 0.4229 
J 0.512 0.730 0.6500 
相关问题