基于列和索引值的比较,如何在 Pandas DataFrame 中更改一个值

6
假设您有一个Pandas DataFrame,其中正文中包含某种数据,列和索引名称中包含数字。
>>> data=np.array([['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']])
>>> columns = [2, 4, 8]
>>> index = [10, 4, 2]
>>> df = pd.DataFrame(data, columns=columns, index=index)
>>> df
    2  4  8
10  a  b  c
4   d  e  f
2   g  h  i

现在假设我们想要根据比较索引和列的方式以某种方式操作数据框。考虑以下内容。
其中,如果索引大于列,则用“k”替换字母:
    2  4  8
10  k  k  k
4   k  e  f
2   g  h  i

当索引等于列时,用“U”替换字母:

    2  4  8
10  k  k  k
4   k  U  f
2   U  h  i

当列大于索引时,用'Y'替换字母:

    2  4  8
10  k  k  k
4   k  U  Y
2   U  Y  Y

为了让问题对所有人都有用:

  • 有什么快速的方法来进行这个替换?

  • 有什么最简单的方法来进行这个替换?

最小化示例的速度结果

  • jezrael: 556 µs ± 66.1 µs每个循环(平均值±7次运行的标准偏差,每个1000个循环)

  • user3471881: 329 µs ± 11.4 µs每个循环(平均值±7次运行的标准偏差,每个1000个循环)

  • thunderwood: 4.65毫秒±252微秒每个循环(平均值±7次运行的标准偏差,每个100个循环)


这是一个重复的问题吗? 我在谷歌上搜索了pandas replace compare index column,并且排名靠前的结果有:

Pandas - 比较两个数据框并替换符合条件的值

Python pandas:基于位置而非索引值替换值

Pandas DataFrame:根据条件替换列中的所有值

然而,我感觉以上三篇文章都没有涉及到是否a)可能或b)如何进行比较。


2
也许我很愚蠢,但是你的期望输出和条件对我来说不匹配。例如:为什么不在索引为10的所有行中添加“k”,为什么索引2大于列2?为什么所有索引为2的行都得到值“Y”? - user3471881
2
@user3471881 完全正确,我想我已经把它修好了。当我检查过时,它看起来是对的...但实际上完全错了。谢谢 +1 - akozi
1
我更新了一下代码,现在应该会更快。 - user3471881
3个回答

9

我认为您需要使用 numpy.select 并带有广播:

m1 = df.index.values[:, None] > df.columns.values
m2 = df.index.values[:, None] == df.columns.values


df = pd.DataFrame(np.select([m1, m2], ['k','U'], 'Y'), columns=df.columns, index=df.index)
print (df)
    2  4  8
10  k  k  k
4   k  U  Y
2   U  Y  Y

性能:

np.random.seed(1000)

N = 1000
a = np.random.randint(100, size=N)
b = np.random.randint(100, size=N)

df = pd.DataFrame(np.random.choice(list('abcdefgh'), size=(N, N)), columns=a, index=b)
#print (df)

def us(df):
    values = np.array(np.array([df.index]).transpose() - np.array([df.columns]), dtype='object')
    greater = values > 0
    less = values < 0
    same = values == 0

    values[greater] = 'k'
    values[less] = 'Y'
    values[same] = 'U'


    return pd.DataFrame(values, columns=df.columns, index=df.index)

def jez(df):

    m1 = df.index.values[:, None] > df.columns.values
    m2 = df.index.values[:, None] == df.columns.values
    return pd.DataFrame(np.select([m1, m2], ['k','U'], 'Y'), columns=df.columns, index=df.index)

In [236]: %timeit us(df)
107 ms ± 358 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [237]: %timeit jez(df)
64 ms ± 299 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

1
如果“columns”和“rows”的长度不同,这段代码会在我的电脑上崩溃。 - user3471881
1
@user3471881 - 谢谢,已添加解决方案 - 通过.values将其转换为numpy数组。在更大的DataFrame中,这样做应该会有更好的性能表现。 - jezrael
1
我会把勾号还给你,原因和我给用户的一样 :) - akozi
1
当您使用np.select时,所有未被m1m2覆盖的点都将填充为值“Y”。如果最终替换为“Y”的过滤器未被使用,您可以展示一个示例,说明如何使用您的答案来输出值“f”、“h”和“i”吗? - akozi
@akozi 我想我也有同样的疑问。我认为你可以将default值保留为df,如果掩码没有改变它们,它将维护数组中的这些值。 df = pd.DataFrame(np.select([m1, m2], ['k','U', 'f'], df), columns=df.columns, index=df.index) - bkeesey
显示剩余3条评论

2

不确定最快的方法是什么,但一种非常简单的方法是直接遍历数据框,如下所示:

for i in df.index:
    for j in df.columns:
        if i>j:
            df.loc[i,j]='k'
        elif j>i:
            df.loc[i,j]='y'
        else:
            df.loc[i,j]='u'

3
这比使用 numpy 慢了大约8倍,就像 @jezrael 展示的那样。 - user3471881
2
真实的,但问题要求以最快和最简单的方式完成。这是如此简单,以至于刚刚学习Python一周的完全初学者也能理解它。 - Thunderwood
我并不是要发表负面评论,只是因为楼主要求而添加了这条。 - user3471881
啊,好的,明白了。 - Thunderwood

1

1. 使用 np.arrays + np.select:

values = np.array(np.array([df.index]).transpose() - np.array([df.columns]))

greater = values > 0
same = values == 0

df = pd.DataFrame(np.select([greater, same], ['k', 'U'], 'Y'), columns=df.columns, index=df.index)

2. 使用 np.arrays 和手动遮罩。

values = np.array(np.array([df.index]).transpose() - np.array([df.columns]), dtype='object')

greater = values > 0
less = values < 0
same = values == 0

values[greater] = 'k'
values[less] = 'Y'
values[same] = 'U'


df = pd.DataFrame(values, columns=df.columns, index=df.index)

谢谢您的回答。您使用哪个软件包来提高速度?我想为我的操作加入不同的速度,并且我很喜欢您的输出结果。 - akozi
我认为现在给这个打上勾号是比较快的,而且大致上复杂度相同。很难量化复杂度,我认为你和Thunderwood的答案阅读起来都不会更困难。 - akozi
@akozi - 你的真实DataFrame有多大?在你的真实数据中表现如何?因为在小数据样本中进行测试应该与在更大的df中不同。 - jezrael
如果不是所有点都被改变了,这个方法是否仍然有效?就我个人而言,我的数据集大约在500x500左右。它们仍然足够小,以至于这些方法之间的速度差异并不是非常明显。 - akozi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接