Как удалить строку с максимальными/минимальными значениями

У меня есть фрейм данных:

   one   N    th
0   A      5      1   
1   Z      17     0   
2   A      16     0   
3   B      9      1   
4   B      17     0   
5   B      117    1   
6   XC     35     1   
7   C      85     0    
8   Ce     965    1 

Я ищу способ продолжать чередовать 0101 в третьем столбце, не удваивая 0 или 1. Итак, я хочу удалить строку с минимальным значением в случае, если у меня есть два повторяющихся 0 в столбце th и максимальное значение, если у меня есть повторяющаяся 1.

Моя база состоит из 1000000 строк.

Я ожидаю, что у меня будет такой фрейм данных:

   one   N    th
0   A      5      1   
1   Z      17     0   
3   B      9      1   
4   B      17     0    
6   XC     35     1   
7   C      85     0    
8   Ce     965    1 

Как это сделать быстрее всего. Я имею в виду векторизованный способ. Мои попытки без результата.

Можете ли вы отредактировать вопрос и добавить ожидаемый результат для этого фрейма данных?

Dogbert 31.07.2024 16:49

Вы забыли опубликовать свою попытку решить хотя бы часть этой проблемы.

Scott Hunter 31.07.2024 16:52

Если вы удаляете дубликат с минимальными значениями, не следует ли удалить строку 6, а не 5?

Michael Cao 31.07.2024 17:25

Хорошо, понятно, вы берете мин/макс в зависимости от 0/1

mozway 31.07.2024 17:45

«Я хочу удалить строку с минимальным значением в случае, если у меня есть два повторяющихся 0 в столбце th и максимальное значение, если у меня есть повторяющаяся 1». Я понимаю, что это означает: для дубликатов с 0 удалите строку с минимальным значением для N (т.е. индексом 2); для дубликатов с 1 удалите строку с максимальным значением для N (т. е. индексом 5).

ouroboros1 31.07.2024 17:46

@Энди, можешь ли ты получить больше двух последовательных 0 или 1?

mozway 31.07.2024 17:55
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
6
97
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

не было бы очень легко просто создать переменную. А затем в цикле установите его на первый, а затем сравните со следующим. Если оно одинаково и равно 0, удалите наименьшее значение; если 1, удалить самое высокое. Если они разные, установите переменную на следующую и продолжите цикл.

Для выполнения работы лучше использовать фрейм данных. Но если бы вы действительно захотели, вы могли бы создать цикл. Я добавлю это как решение, но это определенно не лучший способ сделать это.

michaelt 31.07.2024 19:43
  1. Создайте группу для каждой последовательности 0 и 1, используя сдвиг, чтобы определить начало новой группы.
  2. Выполните преобразование по группам, чтобы определить максимум каждой группы.
  3. Фильтруйте вниз, чтобы принимать только строки с N = max.

df['group'] = (df['th'] != df['th'].shift(1)).cumsum()
df['max'] = df.groupby('group')['N'].transform('max')

df2 = df.loc[df['N'] == df['max']][['one', 'N', 'th']]

Одна версия вкладыша, если вы не хотите создавать промежуточные столбцы:

df.loc[df['N'] == df.groupby((df['th'] != df['th'].shift(1)).cumsum())['N'].transform('max')]
Ответ принят как подходящий

используя обычай groupby.idxmax

Вы можете поменять знак, если «th» равен 1 (чтобы получить максимум вместо минимума), затем настроить собственный группер (с diff или сдвигом + cumsum ) и выполнить groupby .idxmax, чтобы выбрать строки для сохранения:

out = df.loc[df['N'].mul(df['th'].map({0: 1, 1: -1}))
             .groupby(df['th'].ne(df['th'].shift()).cumsum())
             .idxmax()]

Вариант с другим методом замены знака и вычисления группы:

out = df.loc[df['N'].mask(df['th'].eq(1), -df['N'])
             .groupby(df['th'].diff().ne(0).cumsum())
             .idxmax()]

Выход:

  one    N  th
0   A    5   1
1   Z   17   0
3   B    9   1
4   B   17   0
6  XC   35   1
7   C   85   0
8  Ce  965   1

Промежуточные продукты:

  one    N  th  swap  group max
0   A    5   1    -5      1   X
1   Z   17   0    17      2   X
2   A   16   0    16      2    
3   B    9   1    -9      3   X
4   B   17   0    17      4   X
5   B  117   1  -117      5    
6  XC   35   1   -35      5   X
7   C   85   0    85      6   X
8  Ce  965   1  -965      7   X

использование логических масок

Приведенный выше код работает для произвольного количества последовательных 0 или 1. Если вы знаете, что у вас есть только два последовательных индекса, вы также можете использовать логическое индексирование, которое должно быть значительно быстрее:

# has the value higher precedence than the next?
D = df['N'].mask(df['th'].eq(1), -df['N']).diff()

# is the th different from the previous?
G = df['th'].ne(df['th'].shift(fill_value=-1))

# rule for the bottom row
m1 = D.gt(0) | G

# rule for the top row
# same rule as above but shifted up
# D is inverted
# comparison is not strict in case of equality
m2 = ( D.le(0).shift(-1, fill_value=True)
      | G.shift(-1, fill_value=True)
     )

# keep rows of interest
out = df.loc[m1&m2]

Выход:

  one    N  th
0   A    5   1
1   Z   17   0
3   B    9   1
4   B   17   0
6  XC   35   1
7   C   85   0
8  Ce  965   1

Промежуточные продукты:

  one    N  th       D      G     m1     m2  m1&m2
0   A    5   1     NaN   True   True   True   True
1   Z   17   0    22.0   True   True   True   True
2   A   16   0    -1.0  False  False   True  False
3   B    9   1   -25.0   True   True   True   True
4   B   17   0    26.0   True   True   True   True
5   B  117   1  -134.0   True   True  False  False
6  XC   35   1    82.0  False   True   True   True
7   C   85   0   120.0   True   True   True   True
8  Ce  965   1 -1050.0   True   True   True   True

Более сложный пример с равными значениями:

   one    N  th       D      G     m1     m2  m1&m2
0    A    5   1     NaN   True   True   True   True
1    Z   17   0    22.0   True   True   True   True
2    A   16   0    -1.0  False  False   True  False
3    B    9   1   -25.0   True   True   True   True
4    B   17   0    26.0   True   True   True   True
5    B  117   1  -134.0   True   True  False  False
6   XC   35   1    82.0  False   True   True   True
7    C   85   0   120.0   True   True   True   True
8   Ce  965   1 -1050.0   True   True   True   True
9    u  123   0  1088.0   True   True   True   True # because of D.le(0)
10   v  123   0     0.0  False  False   True  False # because or D.gt(0)

Примечание. в случае равенства можно выбрать первую/вторую строку или обе или ничего, в зависимости от используемого оператора (D.le(0), D.lt(0), D.gt(0), D.ge(0)).

тайминги

Несмотря на то, что использование логической маски ограничено максимум двумя последовательными «th», подход с логической маской работает примерно в 4–5 раз быстрее. Рассчитано на 1 миллион строк:

# groupby + idxmax
96.4 ms ± 6.64 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# boolean masks
22.2 ms ± 1.48 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Добавление из-за одного из комментариев. Что касается итеративного способа, описанного ниже, это не тот метод, который вам хотелось бы использовать, поскольку он не использует Pandas. Добавляю его для полноты, так как по сравнению с другими решениями оно менее лаконично.

data = [
    [0, 'A', 5, 1],
    [1, 'Z', 17, 0],
    [2, 'A', 16, 0],
    [3, 'B', 9, 1],
    [4, 'B', 17, 0],
    [5, 'B', 117, 1],
    [6, 'XC', 35, 1],
    [7, 'C', 85, 0],
    [8, 'Ce', 965, 1]
]

df = pd.DataFrame(data, columns=['id', 'one', 'N', 'th'])

def ensure_alternating_th(df):
    while True:
        repeats_found = False
        idx_to_remove = []

        for idx in range(1, len(df)):
            # check for repeated values in 'th' column
            if df.at[idx, 'th'] == df.at[idx - 1, 'th']:
                repeats_found = True
                if df.at[idx, 'th'] == 0:
                    # Drop row with minimum 'N' where 'th' == 0
                    min_row_idx = df.iloc[[idx - 1, idx]]['N'].idxmin()
                elif df.at[idx, 'th'] == 1:
                    # Drop row with maximum 'N' where 'th' == 1
                    max_row_idx = df.iloc[[idx - 1, idx]]['N'].idxmax()
                idx_to_remove.append(min_row_idx if df.at[idx, 'th'] == 0 else max_row_idx)

        if not repeats_found:
            break

        # remove identified rows and reset index
        df = df.drop(idx_to_remove).reset_index(drop=True)

    return df

df_cleaned = ensure_alternating_th(df)

"""
# Returns
   id   one N   th
0   0   A   5   1
1   1   Z   17  0
2   3   B   9   1
3   4   B   17  0
4   6   XC  35  1
5   7   C   85  0
6   8   Ce  965 1
"""

Другие вопросы по теме