A содержит столбцы x, y1, y2, y3 и y4. Мне интересно изучить последовательность {y1,y2,y3,y4} относительно x. Чтобы найти уникальную последовательность {y1,y2,y3,y4}, встречающуюся для каждого x, я делаю следующее:
B = pd.DataFrame()
for x_temp in A['x'].unique():
B = pd.concat([B, A[A['x'] == x_temp][['x','y1','y2','y3','y4']]])
B = B.drop_duplicates().sort_values(by=['x','y1','y2','y3','y4'])
del x_temp
Я хочу представить новый столбец под названием «count» в B, который содержит количество уникальных {y1,y2,y3,y4}, которые произошли для этого конкретного x в A.
B['count'] = A.apply(lambda row: (A['y1'] == row['y1']) & (A['y2'] == row['y2']) & (A['y3'] == row['y3']) & (A['y4'] == row['y4']), axis=1).sum()
Это работает, однако не работает, если A или B имеют пропущенные значения. Я хочу, чтобы пропущенные значения рассматривались как уникальные значения.
Пример:
A = pd.DataFrame({'x':['1','1','1','1','2','2','2','2','2','1'],
'y1':['1','2','2',np.nan,'2',np.nan,'2','2','2','1'],
'y2':['2','1','2','2',np.nan,np.nan,'2','2','1','2'],
'y3':['1','1',np.nan,'2',np.nan,'2',np.nan,np.nan,'1','1'],
'y4':['2','2','2',np.nan,np.nan,'1','2','2','2','2']})
B = pd.DataFrame()
for x_temp in A['x'].unique():
B = pd.concat([B, A[A['x'] == x_temp][['x','y1','y2','y3','y4']]])
B = B.drop_duplicates().sort_values(by=['x','y1','y2','y3','y4'])
del x_temp
B['count'] = A.apply(lambda row: (A['y1'] == row['y1']) & (A['y2'] == row['y2']) & (A['y3'] == row['y3']) & (A['y4'] == row['y4']), axis=1).sum()
print(B)
x y1 y2 y3 y4 count
0 1 1 2 1 2 2
1 1 2 1 1 2 2
2 1 2 2 NaN 2 0
3 1 NaN 2 2 NaN 0
8 2 2 1 1 2 2
6 2 2 2 NaN 2 0
4 2 2 NaN NaN NaN 0
5 2 NaN NaN 2 1 0
Можете ли вы привести пример ввода, вызывающего эту проблему?
Я предполагаю, что это может сработать. Однако это занимает больше времени. В любом случае спасибо.
@JoshuaRoy, твой результат неясен, почему у тебя 2 во второй строке? 1/2/1/1/2
только один, или вы тоже учитываете x=2?
Вы можете использовать notna()
и isna()
, создать свои маски, а затем apply()
:
import pandas as pd
import numpy as np
def _unique(A):
B = pd.DataFrame()
for el in A['x'].unique():
B = pd.concat([B, A[A['x'] == el][['x', 'y1', 'y2', 'y3', 'y4']]])
B = B.drop_duplicates().sort_values(by=['x', 'y1', 'y2', 'y3', 'y4'])
def _mask(row):
y1_mask = A['y1'].fillna('missing') == row['y1'] if pd.notna(row['y1']) else A['y1'].isna()
y2_mask = A['y2'].fillna('missing') == row['y2'] if pd.notna(row['y2']) else A['y2'].isna()
y3_mask = A['y3'].fillna('missing') == row['y3'] if pd.notna(row['y3']) else A['y3'].isna()
y4_mask = A['y4'].fillna('missing') == row['y4'] if pd.notna(row['y4']) else A['y4'].isna()
x_mask = A['x'] == row['x']
return (x_mask & y1_mask & y2_mask & y3_mask & y4_mask).sum()
B['count'] = B.apply(_mask, axis=1)
return B
A = pd.DataFrame({
'x': [1, 1, 2, 2, 2],
'y1': [1, 1, 2, np.nan, 3],
'y2': [2, 2, np.nan, 3, 3],
'y3': [3, 3, 4, 4, 4],
'y4': [4, 4, 5, 5, np.nan]
})
print(_unique(A))
x y1 y2 y3 y4 count
0 1 1.0 2.0 3 4.0 2
2 2 2.0 NaN 4 5.0 1
4 2 3.0 3.0 4 NaN 1
3 2 NaN 3.0 4 5.0 1
Спасибо, это работает. Однако это занимает больше времени, чем требуется. Но спасибо за эту работу.
Предполагая, что вы хотите подсчитать значения, а затем получить сумму по разным xs, вы можете использовать:
cols = ['x','y1','y2','y3','y4']
out = (A[cols].value_counts(dropna=False, sort=False)
.reset_index(name='count')
.sort_values(by=cols, na_position='last')
.assign(count=lambda x: x.groupby(cols[1:], dropna=False)
['count'].transform('sum')
)
)
Выход:
x y1 y2 y3 y4 count
0 1 1 2 1 2 2
1 1 2 1 1 2 2
2 1 2 2 NaN 2 3
3 1 NaN 2 2 NaN 1
4 2 2 1 1 2 2
5 2 2 2 NaN 2 3
6 2 2 NaN NaN NaN 1
7 2 NaN NaN 2 1 1
Если вы хотите установить строки с NaN как 0:
cols = ['x','y1','y2','y3','y4']
out = (A[cols].value_counts(dropna=False, sort=False)
.reset_index(name='count')
.sort_values(by=cols, na_position='last')
.assign(count=lambda x: x.groupby(cols[1:])
['count'].transform('sum')
.fillna(0).convert_dtypes()
)
)
Выход:
x y1 y2 y3 y4 count
0 1 1 2 1 2 2
1 1 2 1 1 2 2
2 1 2 2 NaN 2 0
3 1 NaN 2 2 NaN 0
4 2 2 1 1 2 2
5 2 2 2 NaN 2 0
6 2 2 NaN NaN NaN 0
7 2 NaN NaN 2 1 0
Если вы не хотите суммировать по xs:
cols = ['x','y1','y2','y3','y4']
out = (A[cols].value_counts(dropna=False, sort=False)
.reset_index(name='count')
.sort_values(by=cols, na_position='last')
)
Выход:
x y1 y2 y3 y4 count
0 1 1 2 1 2 2
1 1 2 1 1 2 1
2 1 2 2 NaN 2 1
3 1 NaN 2 2 NaN 1
4 2 2 1 1 2 1
5 2 2 2 NaN 2 2
6 2 2 NaN NaN NaN 1
7 2 NaN NaN 2 1 1
Ух ты... лучший метод, поскольку на вычисление моего большого набора данных ушло <1 секунды. Спасибо.
@Джошуа, так какая версия тебе нужна?
извините за поздний ответ... последний.
Пытаться:
def count_duplicated(g):
vc = g.agg(tuple, axis=1).value_counts()
out = pd.DataFrame([[*k, v] for k, v in vc.items()], columns=[*g.columns, "count"])
return out
B = (
A.groupby("x")
.apply(count_duplicated, include_groups=False)
.droplevel(1)
.reset_index()
)
print(B)
Распечатки:
x y1 y2 y3 y4 count
0 1 1 2 1 2 2
1 1 2 1 1 2 1
2 1 2 2 NaN 2 1
3 1 NaN 2 2 NaN 1
4 2 2 2 NaN 2 2
5 2 2 NaN NaN NaN 1
6 2 NaN NaN 2 1 1
7 2 2 1 1 2 1
Можете ли вы использовать
.fillna()
для замены пропущенных значений контрольным показателем, чтобы сравнение работало?