【问题标题】:pandas: select certain amount of rows based on column ranking using looppandas:使用循环根据列排名选择一定数量的行
【发布时间】:2020-07-06 13:24:18
【问题描述】:

我有一个看起来像这样的数据框

pd.DataFrame({'a':['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
              'b':['N', 'Y', 'Y', 'N', 'Y', 'N', 'Y', 'N', 'N', 'Y'],
              'c':[4, 5, 9, 8, 1, 3, 7, 2, 6, 10]})

   a  b   c
0  A  N   4
1  B  Y   5
2  C  Y   9
3  D  N   8
4  E  Y   1
5  F  N   3
6  G  Y   7
7  H  N   2
8  I  N   6
9  J  Y  10

在 10 行中,我想根据以下条件选择 5 行:

“c”列是我的排名列。

  1. 选择排名最低的 2 行(已选择第 4 行和第 7 行)
  2. 选择列 'b' = 'Y' AND rank
  3. 如果使用上述标准选择的行数少于 5 行,则剩余的未平仓头寸应按排名顺序(最低)填充,其中 'b' = 'Y' 的行和排名
  4. 如果通过前 3 个条件的行少于 5 行,则按排名顺序(最低)填充剩余位置,其中 'b' = 'N'

我已经尝试过这个(包括规则 1 和 2),但正在努力从那里继续下去

df['selected'] = ''
df.loc[(df.c <= 2), 'selected'] = 'rule_1'
df.loc[((df.c <= 5) & (df.b == 'Y')), 'selected'] = 'rule_2'

我的结果数据框应该是这样的

   a  b   c  selected
0  A  N   4     False
1  B  Y   5     rule_2
2  C  Y   9     False
3  D  N   8     rule_4
4  E  Y   1     rule_1
5  F  N   3     False
6  G  Y   7     rule_3
7  H  N   2     rule_1
8  I  N   6     False
9  J  Y  10     False

基于下面 Vinod Karantothu 提供的解决方案,我选择了以下似乎可行的解决方案:

def solution(df):

    def sol(df, b='Y'):
        result_df_rule1 = df.sort_values('c')[:2]
        result_df_rule1['action'] = 'rule_1'
        result_df_rule2 = df.sort_values('c')[2:].loc[df['b'] == b].loc[df['c'] <= 5]
        result_df_rule2['action'] = 'rule_2'
        result = pd.concat([result_df_rule1, result_df_rule2]).head(5)

        if len(result) < 5:
            remaining_rows = pd.concat([df, result, result]).drop_duplicates(subset='a', keep=False)
            result_df_rule3 = remaining_rows.loc[df['b'] == b].loc[df['c'] <= 7]
            result_df_rule3['action'] = 'rule_3'
            result = pd.concat([result, result_df_rule3]).head(5)
            return result, pd.concat([remaining_rows, result, result]).drop_duplicates(subset='a', keep=False)

    result, remaining_data = sol(df)

    if len(result) < 5:
        result1, remaining_data = sol(remaining_data, 'N')
        result1['action'] = 'rule_4'
        result = pd.concat([result, result1]).head(5).drop_duplicates(subset='a', keep=False).merge(df, how='outer', on='a')

    return result

if __name__ == '__main__':
df = pd.DataFrame({'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
                   'b': ['N', 'Y', 'Y', 'N', 'Y', 'N', 'Y', 'N', 'N', 'Y'],
                   'c': [4, 5, 9, 8, 1, 3, 7, 2, 6, 10]})

    result = solution(df)
    print(result)

【问题讨论】:

  • 这听起来工作量很大,你能告诉我们你的尝试和失败,所以我们有一些起始位置吗?
  • 如果规则 1 或 2 有多个匹配项,输出中的行顺序应该是什么?在原始 df 中按排名还是按顺序?
  • 每一行或基本上'a'列中的每个字母只能选择一次。因此,如果在规则 1 中选择它,则规则 2 会忽略它
  • 好的,我的意思是在规则范围内。假设您有 2 行匹配规则 2 而不是规则 1。它们应按什么顺序包含?基于数据框中的排名或第一个外观?
  • 啊好的。基于等级(但不应该真的很重要,因为 df 的构造方式是规则 1 和 2 总共不应该选择超过 5 个)。请查看我对原始帖子的编辑。这应该涵盖规则 1 和 2。我正在努力解决规则 3 和 4。

标签: python pandas loops rank


【解决方案1】:
import pandas as pd

def solution(df):

    def sol(df, b='Y'):
        result_df_rule1 = df.sort_values('c')[:2]
        result_df_rule2 = df.sort_values('c')[2:].loc[df['b'] == b].loc[df['c'] <= 5]
        result = pd.concat([result_df_rule1, result_df_rule2]).head(5)

        if len(result) < 5:
            remaining_rows = pd.concat([df, result, result]).drop_duplicates(keep=False)
            result_df_rule3 = remaining_rows.loc[df['b'] == b].loc[df['c'] <= 7]
            result = pd.concat([result, result_df_rule3]).head(5)
            return result, pd.concat([remaining_rows, result, result]).drop_duplicates(keep=False)

    result, remaining_data = sol(df)

    if len(result) < 5:
        result1, remaining_data = sol(remaining_data, 'N')
        result = pd.concat([result, result1]).head(5)

    return result

if __name__ == '__main__':
    df = pd.DataFrame({'a':['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
              'b':['N', 'Y', 'Y', 'N', 'Y', 'N', 'Y', 'N', 'N', 'Y'],
              'c':[4, 5, 9, 8, 1, 3, 7, 2, 6, 10]})
    
    result = solution(df)
    print(result)

结果:

   a  b  c
4  E  Y  1
7  H  N  2
1  B  Y  5
6  G  Y  7
5  F  N  3

【讨论】:

  • 谢谢。我同意你的回答并稍作修改。仍然认为虽然可以稍微缩短代码。但功能绝对是要走的路
【解决方案2】:

对于您的 4th RULE,您在生成的数据框中提到,ROW_INDEX 3 将会出现,但它的排名为 8,即不是最低的,ROW_INDEX 5应该根据你给的RULES来:

import pandas as pd
data = pd.DataFrame({'a':['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
          'b':['N', 'Y', 'Y', 'N', 'Y', 'N', 'Y', 'N', 'N', 'Y'],
          'c':[4, 5, 9, 8, 1, 3, 7, 2, 6, 10]})

data1 = data.nsmallest(2, ['c'])
dataX = data.drop(data1.index)

data2 = dataX[((dataX.b == "Y") & (dataX.c<=5))] 
dataX = dataX.drop(data2.index) 

data3 = dataX[((dataX.b == "Y") & (dataX.c<=7))]  
dataX = dataX.drop(data3.index) 

data4 = dataX[((dataX.b == "N"))]
data4 = data4.nsmallest(1, ['c'])

resultframes = [data1, data2, data3, data4]
resultfinal = pd.concat(resultframes)
print(resultfinal)

这是输出:

   a  b  c
4  E  Y  1
7  H  N  2
1  B  Y  5
6  G  Y  7
5  F  N  3

【讨论】:

    【解决方案3】:

    您可以为规则创建额外的列,然后排序并获取头部。来自 cmets 的 IIUC 规则 3 已经涵盖了规则 2,因此无需单独计算。

    df['r1'] = df.c < 3
    df['r3'] = (df.c <= 7) & (df.b == 'Y')
    print(df.sort_values(['r1', 'r3', 'c'], ascending=[False, False, True])[['a', 'b', 'c']].head(5))
    
       a  b  c
    4  E  Y  1
    7  H  N  2
    1  B  Y  5
    6  G  Y  7
    5  F  N  3
    

    在布尔列上排序有效,因为True &gt; False

    注意:您可能需要使用不同的数据集根据您的期望调整代码。例如,您的最后一行 9 J Y 10 目前不受任何规则的约束。您可以采用这种方法并在需要时对其进行扩展。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-09-23
      • 1970-01-01
      • 1970-01-01
      • 2018-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多