Это мой 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]
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 для связанного вопроса, я бы, вероятно, использовал цифру
Для изменения исходного решения измените 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'])])
Большое спасибо. Можно ли использовать тот же подход для сообщения по связанному ответу? Я просто хочу знать.