Pandas - 根据其他列的值选择特定列的行值

4
这有点复杂,但我会展示我的数据。
我构建了以下数据框:
      Mid_XYZ  Mid_YYY  Mid_ZZZ Select1 Select2
867    1019.11   1027.64  1022.68   XYZ   YYY
873    1018.04   1027.58  1022.81   XYZ   ZZZ

我希望能够根据Select1Select2字符串选择列中的值,并通过匹配部分列名来实现。 在第一行中,这将是1019.111027.64(列 Mid_XYZ Mid_YYY )- 因为Select1具有字符串 XYZ ,而Select2具有字符串 YYY

在第二行中,则是1018.041022.81(列 Mid_XYZ Mid_ZZZ )。

稍后,我计划将这些值的总和存储在新的列中。数据框将看起来像这样:

      Mid_XYZ  Mid_YYY  Mid_ZZZ Select1 Select2 Sum
867    1019.11   1027.64  1022.68   XYZ   YYY   2046.75
873    1018.04   1027.58  1022.81   XYZ   ZZZ   2040.85

我可以将列名更改为完全匹配,但是否有一些使用正则表达式的解决方案呢?我知道df.filter(regex='XYZ'),但如何逐行执行该操作?

4个回答

5
请使用以下向量化解决方案:
import numpy as np

# clean rows
clean = df.columns.str.replace('^Mid_', '', regex=True)

# find matching column indices
s1 = np.argmax(clean.values == df['Select1'].values[:, None], axis=1)
s2 = np.argmax(clean.values == df['Select2'].values[:, None], axis=1)

# index and sum
df['Sum'] = df.values[np.arange(len(s1)), s1] + df.values[np.arange(len(s2)), s2]

print(df)

输出

     Mid_XYZ  Mid_YYY  Mid_ZZZ Select1 Select2      Sum
867  1019.11  1027.64  1022.68     XYZ     YYY  2046.75
873  1018.04  1027.58  1022.81     XYZ     ZZZ  2040.85

2
import pandas as pd

如果您有以下内容:
df=pd.DataFrame.from_dict({'Mid_XYZ':[1019.11,1018.04],
                           'Mid_YYY':[1027.64,1027.58],
                           'Mid_ZZZ':[1022.68,1022.81],
                           'Select1':['XYZ','XYZ'],
                           'Select2':['YYY','ZZZ']})

您可以做以下事情:

df['Sum']=df.apply(lambda row:
                   row['Mid_'+row['Select1']]+\
                   row['Mid_'+row['Select2']],
                   axis=1)

df将会是:

   Mid_XYZ  Mid_YYY  Mid_ZZZ Select1 Select2      Sum
0  1019.11  1027.64  1022.68     XYZ     YYY  2046.75
1  1018.04  1027.58  1022.81     XYZ     ZZZ  2040.85

如果你不喜欢lambda函数,可以通过定义一个函数来实现相同的结果:

def sumfunc(row):
    return row['Mid_'+row['Select1']]+row['Mid_'+row['Select2']]

然后:

df['Sum']=df.apply(sumfunc,axis=1)

这个答案不错,但它没有向量化...无论如何投票支持 :) - adir abargil
谢谢!我也给向量化的答案投了赞成票。当我的数据框不是非常大时,我更喜欢可读性而不是向量化,并且我发现这个解决方案更易读。当然,这只是一个观点 :) - zabop

2

使用 meltconcat 的另一种解决方案:

cols = ['Select1', 'Select2']
df1 = df.melt(id_vars=cols, ignore_index=False)
df['Sum'] = (pd.concat([df1[('Mid_' + df1[col]) == df1['variable']] 
                        for col in cols]).groupby(level=0).sum())  # can also pass `sort=False` to `groupby` for ~10% or something speed boost
df
Out[1]: 
     Mid_XYZ  Mid_YYY  Mid_ZZZ Select1 Select2      Sum
867  1019.11  1027.64  1022.68     XYZ     YYY  2046.75
873  1018.04  1027.58  1022.81     XYZ     ZZZ  2040.85

2

除了@Dani Mesejo的答案外,我还添加了一些更快的实现和更直接的使用numpy内置where函数...

我的实现是vec2

def vec1(df):
    clean = df.columns.str.replace('^Mid_', '', regex=True)
    s1 = np.argmax(clean.values == df['Select1'].values[:, None], axis=1)
    s2 = np.argmax(clean.values == df['Select2'].values[:, None], axis=1)
    # index and sum
    df['Sum'] = df.values[np.arange(len(s1)), s1] + df.values[np.arange(len(s2)), s2]
    return df

def vec2(df):
    clean = df.columns.str.replace('^Mid_', '', regex=True)
    idx1 = np.where(clean.values == df['Select1'].values[:,None] )
    idx2 = np.where(clean.values == df['Select2'].values[:,None] )
    df['Sum'] = df.values[idx1] + df.values[idx2]
    return df

以下是时间比较:

我的实现方式:

%timeit vec2(df) : 388 µs ± 3.97 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

@Dani Mesejo :

%timeit vec1(df) : 405 µs ± 6.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

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