Как я могу сравнить значение в одном столбце со всеми значениями, которые находятся ДО него в другом столбце, чтобы найти количество уникальных значений, которые меньше?

Это мой DataFrame:

import pandas as pd
df = pd.DataFrame(
    {
        'a': [100, 100, 105, 106, 106, 107, 108, 109],
        'b': [99, 100, 110, 107, 100, 110, 120, 106],
    }
)

Ожидаемый результат — создание столбца x:

     a    b    x
0  100   99    0
1  100  100    1
2  105  110    2
3  106  107    3
4  106  100    1
5  107  110    4
6  108  120    5
7  109  106    3

Логика:

Это своего рода продолжение этого ответа. Объясняю логику на примерах и начинаю со строки 1:

Для строки 1 значение столбца b равно 100. Затем, чтобы получить x для строки 1, это значение необходимо сравнить со всеми УНИКАЛЬНЫМИ значениями в a, которые находятся в той же строке или перед ней, чтобы выяснить, на сколько значений в a меньше. чем или равен ему. Единственное уникальное значение, находящееся в той же строке или до него, равно 100, поэтому для x выбирается 1.

Для строки 2 в a есть два уникальных значения, которые меньше или равны 110: 100, 105.

Для остальных строк логика та же.

Это моя попытка, основанная на связанном ответе, но она не работает:

t = df.a.unique()
m1 = np.arange(len(t))[:,None] >= np.arange(len(t))

h = df['b'].to_numpy()
m2 = t <= h[:, None]
Почему в 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
0
92
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

IIUC, используйте numpy широковещание, tril и маску, чтобы сохранить только уникальные значения:

a = df['a'].mask(df['a'].duplicated()).to_numpy()
b = df['b'].to_numpy()
df['x'] = np.tril(b[:, None]>=a).sum(axis=1)

В качестве однострочника:

df['x'] = np.tril(df[['b']].to_numpy() >=
                   df['a'].mask(df['a'].duplicated()).to_numpy()).sum(axis=1)

Выход:

     a    b  x
0  100   99  0
1  100  100  1
2  105  110  2
3  106  107  3
4  106  100  1
5  107  110  4
6  108  120  5
7  109  106  3

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

# a
array([100.,  nan, 105., 106.,  nan, 107., 108., 109.])

# b
array([ 99, 100, 110, 107, 100, 110, 120, 106])

# np.tril(b[:, None]>=a)
array([[False, False, False, False, False, False, False, False],
       [ True, False, False, False, False, False, False, False],
       [ True, False,  True, False, False, False, False, False],
       [ True, False,  True,  True, False, False, False, False],
       [ True, False, False, False, False, False, False, False],
       [ True, False,  True,  True, False,  True, False, False],
       [ True, False,  True,  True, False,  True,  True, False],
       [ True, False,  True,  True, False, False, False, False]])

Большое спасибо. Можно ли использовать тот же подход для сообщения по связанному ответу? Я просто хочу знать.

AmirX 24.06.2024 09:28

@AmirX для связанного вопроса, я бы, вероятно, использовал цифру

mozway 24.06.2024 09:35

Для изменения исходного решения измените t для замены повторяющихся значений на NaN и для подсчета True используйте sum обеих масок:

t = df['a'].mask(df['a'].duplicated()).to_numpy()
m1 = np.arange(len(t))[:,None] >= np.arange(len(t))

h = df['b'].to_numpy()
m2 = h[:, None] >= t

df['x'] = (m1 & m2).sum(axis=1)
print (df)
     a    b  x
0  100   99  0
1  100  100  1
2  105  110  2
3  106  107  3
4  106  100  1
5  107  110  4
6  108  120  5
7  109  106  3

Для огромных и разреженных наборов данных используйте: from scipy.sparse import csr_matrix

import pandas as pd
import numpy as np
from scipy.sparse import csr_matrix

# Sample DataFrame
df = pd.DataFrame({
    'a': [100, 100, 105, 106, 106, 107, 108, 109],
    'b': [99, 100, 110, 107, 100, 110, 120, 106],
})

a= df['a'].mask(df['a'].duplicated()).to_numpy()
b= df['b'].to_numpy()

sparse_b  = csr_matrix(b[:,None])

comparison = sparse_b >= a
comparison = csr_matrix(comparison)

tril_indices = (rows,cols) = np.tril_indices(len(b))

tril_row_indices = tril_indices[0]

lower_tril_mask = np.ones(len(tril_indices[0]), dtype=bool)

lower_tril_sparse_matrix = csr_matrix((lower_tril_mask,tril_indices))

res_matrix = comparison.multiply(lower_tril_sparse_matrix)

res =  res_matrix.sum(axis=1).A.flatten()
print(res)#[0 1 2 3 1 4 5 3]

df['res'] =res 
print(df)
'''
    a    b  res
0  100   99    0
1  100  100    1
2  105  110    2
3  106  107    3
4  106  100    1
5  107  110    4
6  108  120    5
7  109  106    3
'''

Обычный метод numpy 1:

import pandas as pd
import numpy as np

# Sample DataFrame
df = pd.DataFrame({
    'a': [100, 100, 105, 106, 106, 107, 108, 109],
    'b': [99, 100, 110, 107, 100, 110, 120, 106],
})
a = df['a'].to_numpy()
b = df['b'].to_numpy()
    
# Create a mask to mark duplicates in 'a' as NaN, so they are ignored in comparison
masked_a = np.where(pd.Series(a).duplicated(), np.nan, a)
df['masked_a'] = masked_a    

tril_matrix = np.tril(np.ones((len(b), len(b)), dtype=bool))
 
comparison_matrix = (b[:, None] >= masked_a) & tril_matrix

df['x'] = comparison_matrix.sum(axis=1)
print(df)
'''
    a    b  masked_a  x
0  100   99     100.0  0
1  100  100       NaN  1
2  105  110     105.0  2
3  106  107     106.0  3
4  106  100       NaN  1
5  107  110     107.0  4
6  108  120     108.0  5
7  109  106     109.0  3
'''

Обычный метод Numpy 2:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'a': [100, 100, 105, 106, 106, 107, 108, 109],
    'b': [99, 100, 110, 107, 100, 110, 120, 106],
})

a = df['a'].mask(df['a'].duplicated()).to_numpy()
b = df['b'].to_numpy()

comparison = (b[:,None] >= a)

tril_indices =(row, col) = np.tril_indices(len(b))
print(tril_indices)

lower_tril_mask = np.zeros_like(comparison)
print(lower_tril_mask) 

lower_tril_mask[tril_indices] = True
print(lower_tril_mask) 

res_matrix = comparison*lower_tril_mask
print(res_matrix)

res_matrix_sum = res_matrix.sum(axis=1)
print(res_matrix_sum)#[0 1 2 3 1 4 5 3]

df['res_matrix_sum'] = res_matrix_sum
print(df)
'''
     a    b  res_matrix_sum
0  100   99               0
1  100  100               1
2  105  110               2
3  106  107               3
4  106  100               1
5  107  110               4
6  108  120               5
7  109  106               3
'''

Это должно быть медленнее, чем решения numpy, но вот решение pandas:

df.assign(x = [(a.unique() <= b).sum() for a,b in zip(df['a'].expanding(),df['b'])])

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