pandas 相互比较DataFrame中的所有列

5cg8jx4n  于 2023-02-02  发布在  其他
关注(0)|答案(4)|浏览(130)

假设这是我的 Dataframe :

id    c1    c3    c5    c7
1     10    11    12    13
2     10    14    11    13
3     15    14    12    10

我想将每列与每行的下一列进行比较。
如果c1〉c3且c3〉c5,以此类推,则该行的结果为1,如果c1〈c3且c3〈c5,以此类推,则该行的结果为-1,否则结果应为0。
这可以通过一个简单的循环来完成,如下所示:

number_of_rows = df.shape(0)
for i in range(0, number_of_rows):
   if df[i, c1] < df[i, c3] and df[i, c3] < df[i, c5] and df[i, c5] < df[i, c7]:
       df[i, 'result'] = -1
   elif df[i, c1] > df[i, c3] and df[i, c3] > df[i, c5] and df[i, c5] > df[i, c7]:
       df[i, 'result'] = 1
   else:
       df[i, 'result'] = 0

预期结果应该是这样的 Dataframe :

id    result
1     1
2     0
3     -1

我觉得一定有更快/更干净的方法来做到这一点,使用Pandas功能。
另外,我不确定这是否有帮助,但如您所见,列名具有一种模式

lp0sw83n

lp0sw83n1#

可以使用df.diff查看增量/减量:

diff = df.diff(axis=1)

#checks if all differences are positive
c1 = diff.fillna(1).ge(1).all(1)

#checks if all differences are negative
c2 = diff.fillna(-1).lt(0).all(1)

#assign
df['result'] = np.select([c1,c2],[1,-1],0)
print(df)

    c1  c3  c5  c7  result
id                        
1   10  11  12  13       1
2   10  14  11  13       0
3   15  14  12  10      -1
zmeyuzjn

zmeyuzjn2#

import numpy as np
import pandas as pd

# Your df here:
df = pd.read_clipboard().set_index("id")

out = np.select([
    df.agg(lambda x: x.is_monotonic_increasing, axis=1),
    df.agg(lambda x: x.is_monotonic_decreasing, axis=1)
], [1, -1])

输出:

array([ 1,  0, -1])
htrmnn0y

htrmnn0y3#

简单的单线解决方案

df['result'] = df.apply(lambda x: 1 if x.c1>x.c3>x.c5>x.c7 else( -1 if x.c1<x.c3<x.c5<x.c7 else 0),axis=1)

输出:-

df.result
0   -1
1    0
2    1
Name: result, dtype: int64
hk8txs48

hk8txs484#

TL;医生:

df['result'] = ((df.c1>df.c3) & (df.c3>df.c5) & (df.c5>df.c7))*1 + ((df.c1<df.c3) & (df.c3<df.c5) & (df.c5<df.c7))*-1

永远不要迭代行。这是Pandas的第一条规则。
python是一种非常慢的语言,它之所以也是目前最流行的语言,以及许多非常快的应用程序都是用python编写的,是因为python程序员会尽一切努力使耗时的代码不在python中。
而这样做的方法是使用panda和numpy这样的库,这些库不是用python写的,来做繁重的计算。在处理大量数据的情况下,耗时的代码是你应用在一个大 Dataframe 的所有行上的代码;因此您使用一个库,它将对所有行执行迭代,并为您对每一行执行计算。
如果你使用pandom但仍然自己迭代行,你就错过了重点,在这种情况下,你最好简单地使用python列表或字典。
请注意,apply也是如此:这只是下一件不要做的事情。自己编写for循环仍然更好,因为至少for循环是在Pandas代码内部编写的,但仍然,你在每一行上所做的仍然是Python代码。
所以,在你的例子中,你必须找到一种方法来批量计算整列(注意,我假设你的行数比列数多......不像你的例子, Dataframe 通常是这样)。
例如,df.c1 > df.c3计算布尔值数组
你可以把这些结合起来,得到你想要的东西

df['result'] = ((df.c1>df.c3) & (df.c3>df.c5) & (df.c5>df.c7))*1 + ((df.c1<df.c3) & (df.c3<df.c5) & (df.c5<df.c7))*-1

一些计时注意事项

对于只有3行的情况,批处理计算的开销使得基于for循环的(您的解决方案)仍然更快。
但是对于更大的行数会显示差异。请参见下表
| 方法|定时3行|1000行|10k行|10万行|1个月|10个月|
| - ------|- ------|- ------|- ------|- ------|- ------|- ------|
| 您的(用于循环)|1.3毫秒|394毫秒|3808毫秒|38400毫秒|||
| 金藻(agg)|2.4毫秒|140毫秒|1290毫秒|13800毫秒|||
| Nehal's(适用)|1.0毫秒|71毫秒|695毫秒|6930毫秒|||
| 安基的|2.6毫秒|2.8毫秒|4.2毫秒|18毫秒|220毫秒|2300毫秒|
| 我的(df ['c1 ']〉df ['c3']...)|2.0毫秒|2.0毫秒|2.2毫秒|7.5毫秒|33毫秒|400毫秒|
| 我的第二个(变量编号列)|2.8毫秒|2.9毫秒|3.2毫秒|9.4毫秒|52毫秒|480毫秒|
注意,394/1.3等于303,所以行比几乎等于333,你的解是O(n),它已经显示出来了。
Chrysophylax(和其他基于lambda的解决方案)已经更快了。因为它节省了python for循环本身,但没有节省在for循环中所做的纯python执行。同样,panda中最糟糕的错误是迭代for循环。第二糟糕的错误是使用lambda。如果n足够大,你会从它那里得到3倍的增益。
我的解决方案,以及其他非for、非基于lambda的解决方案也是O(n)-没有免费餐,任何方法,无论是否基于Pandas,在每一行上计算的东西都必须至少为O但即使在n = 10000时,它也不会显示出来,因为它太快了,即使在n = 10000时,它仍然是我们所支付的开销,计算本身仍然可以忽略不计。对于n = 100000,时间仍然只有7. 5 ms(它终于开始增加,但仍然不成比例)。
因此,对于足够大的数据,增益至少为× 5000。
编辑:我在anky的解决方案发布之前写了这篇文章。注意,他们的解决方案也避免了for和lambda,所以它有同样的时间因素。当n = 100000时,他们的代码在我的计算机上运行一次需要18毫秒。当然,这比我的7.5毫秒要慢。但数量级是一样的。另外,他们的解决方案比我的解决方案有一个重要的优势:它可以在不涉及任何列数的代码的情况下工作(对于我的代码,您必须向代码中添加一些& (df.c7 > df.c9)才能添加c9列)

编辑(回复您的评论):

如果列数是可变的,你可以使用Anky的解,在这种情况下是有效的。
或者修改我的,在列上进行迭代

import itertools
def varNumCol():
    cols=df.columns
    r1=pd.Series(True, df.index)
    r2=pd.Series(True, df.index)
    for c1, c2 in  itertools.pairwise(cols):
        r1 &= df[c1]>df[c2]
        r2 &= df[c1]<df[c2]
    df['result'] = r1*1 - r2*1

它比我之前的答案稍微慢了一点,但是它仍然比任何其他的解决方案都快(包括,我必须承认,我没有预料到,Anky的)。我已经更新了时间表来添加它。
(Note我习惯使用itertools,但这与性能无关。列的迭代对于正常使用来说是可以忽略的。因此,您还可以使用更传统的索引进行迭代,例如

for k in range(len(cols)-1):
    c1=cols[k]
    c2=cols[k+1]
    ...

在这种情况下,更多的是出于美学考虑而不是性能考虑)

相关问题