pandas: выберите определенное количество строк на основе ранжирования столбцов с помощью цикла

У меня есть кадр данных, который выглядит так

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» И ранг ‹ = 5 (выбрана строка 1)
  3. в случае выбора менее 5 строк с использованием вышеуказанных критериев оставшиеся открытые позиции должны быть заполнены в порядке ранжирования (самый низкий) строками, где «b» = «Y» и которые имеют ранг ‹ = 7 (выбрана строка 6)
  4. в случае, если менее 5 строк соответствуют первым 3 критериям, заполните оставшиеся позиции в порядке ранжирования (самый низкий), где «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

на основе решений, предоставленных Винодом Карантоту ниже, я выбрал следующее, которое, похоже, работает:

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)

person idt_tt    schedule 06.07.2020    source источник
comment
Это звучит как много работы, можете ли вы показать нам, что вы пытались и потерпели неудачу, чтобы у нас была начальная позиция, пожалуйста?   -  person Celius Stingher    schedule 06.07.2020
comment
каким должен быть порядок строк в выводе, если у вас есть несколько совпадений для правила 1 или 2? По рангу или по порядку в исходном df?   -  person ScootCork    schedule 06.07.2020
comment
каждая строка или в основном каждая буква в столбце «а» может быть выбрана только один раз. поэтому, если он выбран в правиле 1, он игнорируется для правила 2   -  person idt_tt    schedule 06.07.2020
comment
Хорошо, то, что я имел в виду, было в пределах правила. Скажем, у вас есть 2 строки, соответствующие правилу 2, а не правилу 1. В каком порядке их следует включать? на основе ранга или первого появления в кадре данных?   -  person ScootCork    schedule 06.07.2020
comment
Ах хорошо. на основе ранга (но это не должно иметь большого значения, поскольку df построен таким образом, что для правил 1 и 2 никогда не должно быть выбрано более 5). Пожалуйста, посмотрите мое редактирование моего исходного сообщения. это должно охватывать правило 1 и 2. Я борюсь с правилом 3 и 4.   -  person idt_tt    schedule 06.07.2020


Ответы (3)


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
person Vinod Karantothu    schedule 06.07.2020
comment
Спасибо. я пошел с вашим ответом и немного изменил его. все еще думаю, хотя можно немного сократить код. но функция определенно путь - person idt_tt; 07.07.2020

Для вашего 4-го ПРАВИЛА, которое вы упомянули в результирующем кадре данных, появится ROW_INDEX 3, но его ранг 8 не является самым низким, ROW_INDEX 5 должен быть получен в соответствии с указанными вами ПРАВИЛАМИ:

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
person Shubham Thakur    schedule 06.07.2020

Вы можете создать дополнительные столбцы для правил, затем отсортировать и взять голову. 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 > False.

Примечание. Возможно, вам придется настроить код в соответствии с вашими ожиданиями с другими наборами данных. Например, на вашу последнюю строку 9 J Y 10 в настоящее время не распространяется ни одно из правил. Вы можете воспользоваться этим подходом и расширить его при необходимости.

person ScootCork    schedule 06.07.2020