根据条件从数据框中选择行

Selecting rows from a dataframe based on conditions

我有一个数据框:

>>> d = {'ID' : ['ABC', 'ABC', 'ABC', 'DFG', 'DFG', 'DFG', 'EGF', '2BD', '2BD'], 'Val': ['High', 'Low', 'High', 'High', 'High', 'Low', 'Low', 'Low', 'High'], 
... 'Num': [22,2,16,10,50,3,2,34,2], 'Val2':['Low', 'High', 'Low', 'High', 'High', 'High', 'High', 'High', 'High']}
>>> import pandas as pd
>>> df = pd.DataFrame(d)
>>> df
    ID  Num   Val  Val2
0  ABC   22  High   Low
1  ABC    2   Low  High
2  ABC   16  High   Low
3  DFG   10  High  High
4  DFG   50  High  High
5  DFG    3   Low  High
6  EGF    2   Low  High
7  2BD   34   Low  High
8  2BD    2  High  High

有没有办法将条件应用到列中具有相同值的行,然后应用一些条件来检查其他列中的值?

我想要这样的输出:

ID | Val | Num | Val2

ABC | High | 22 | Low
DFG | High | 50 | High
EGF | Low  | 2  | High
2BD | High  | 2  | High

即,对于第一列中的相同 ID,它检查 Val 列,给予 'High' 值高于 'Low' 或 'Mod' 的优先级,然后从该 ID 在 Val 列中具有 'High' 的行 select 在 'Num' 列中具有较高值的​​行。

我是这样做的:

import pandas as pd
d = {'ID' : ['ABC', 'ABC', 'ABC', 'DFG', 'DFG', 'DFG', 'EGF', '2BD', '2BD'], 'Val': ['High', 'Low', 'High', 'High', 'High', 'Low', 'Low', 'Low', 'High'], 'Num': [22,2,16,10,50,3,2,34,2], 'Val2':['Low', 'High', 'Low', 'High', 'High', 'High', 'High', 'High', 'High']}
df = pd.DataFrame(d)
print df

x = df.ID.unique().tolist()
f_df=pd.DataFrame()
idlist=[]
vallist=[]
numlist=[]

for i in x:
    idlist.append(i)
    new_df = df.loc[df['ID'] == i]
    h_df = new_df.loc[df['Val'] == 'High']
    if h_df.empty:
        m_df = new_df.loc[df['Val'] == 'Mod']
        if m_df.empty:
            l_df = new_df.loc[df['Val'] == 'Low']
            vallist.append('Low')
            if len(l_df) > 1:
                m = l_df['Num'].max()
                numlist.append(m)
            else:
                m = l_df['Num'].max()
                numlist.append(m)
        else:
            vallist.append('Mod')
            if len(m_df) > 1:
                m = m_df['Num'].max()
                numlist.append(m)
            else:
                m = m_df['Num'].max()
                numlist.append(m)

    else:
        vallist.append('High')
        if len(h_df) > 1:
            m = h_df['Num'].max()
            numlist.append(m)
        else:
            m = h_df['Num'].max()
            numlist.append(m)

f_df['ID'] = idlist
f_df['Val'] = vallist
f_df['Num'] = numlist

print f_df

有更好的方法吗?另外,如何在输出中也获得 Val2 的相应值?因为我实际上有一个包含 12 列的数据框。

编辑添加

真正的 pandonic 方法是使用 categories:

>>> df['Val'] = df.Val.astype('category').cat.set_categories(['High','Mod','Low'], ordered=True)
>>> df['Val2'] = df.Val2.astype('category').cat.set_categories(['High','Mod','Low'], ordered=True)
>>> df
    ID  Num   Val  Val2
0  ABC   22  High   Low
1  ABC    2   Low  High
2  ABC   16  High   Low
3  DFG   10  High  High
4  DFG   50  High  High
5  DFG    3   Low  High
6  EGF    2   Low  High
7  2BD   34   Low  High
8  2BD    2  High  High
>>> df.dtypes
ID        object
Num        int64
Val     category
Val2    category
dtype: object

所以现在排序按照我们想要的方式进行!

>>> (df.sort_values(['Val','Num'], ascending=[True, False])
...    .groupby('ID')
...    .nth(0))
     Num   Val  Val2
ID
2BD    2  High  High
ABC   22  High   Low
DFG   50  High  High
EGF    2   Low  High

原答案

是的,我认为您不想使用内置排序和 groupby,所以首先,创建映射您的 "High"、"Mod" 和 [= 的列45=] 值到 数字 以便我们可以理智而轻松地使用它们:

>>> df['valmap'] = df.Val.map({'High':0, 'Mod':1, 'Low':2})
>>> df['val2map'] = df.Val2.map({'High':0, 'Mod':1, 'Low':2})
>>> df
    ID  Num   Val  Val2  valmap  val2map
0  ABC   22  High   Low       0        2
1  ABC    2   Low  High       2        0
2  ABC   16  High   Low       0        2
3  DFG   10  High  High       0        0
4  DFG   50  High  High       0        0
5  DFG    3   Low  High       2        0
6  EGF    2   Low  High       2        0
7  2BD   34   Low  High       2        0
8  2BD    2  High  High       0        0

然后我想你只想:

>>> df.sort_values(['valmap','Num'], ascending=[True, False]).groupby('ID').nth(0)
     Num   Val  Val2  val2map  valmap
ID
2BD    2  High  High        0       0
ABC   22  High   Low        2       0
DFG   50  High  High        0       0
EGF    2   Low  High        0       2

当然,您始终可以 select 您特别想要的列:

>>> (df.sort_values(['valmap','Num'], ascending=[True, False])
...    .groupby('ID')['Num','Val', 'Val2']
...    .nth(0))

     Num   Val  Val2
ID
2BD    2  High  High
ABC   22  High   Low
DFG   50  High  High
EGF    2   Low  High

所以,如果您考虑一下您的要求:

"where for the same IDs in the first column, " => 使用 groupby('ID')

"gives high precedence to 'High' value than 'Low' or 'Mod' and then from among the rows for that ID with 'High' in the Val column select the row with higher value in the 'Num' column." => 按 Val 排序,然后按 Num(降序)排序,然后取最高的。