Поиск количества уникальной последовательности, состоящей из значений, охватывающих несколько столбцов в Python

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

Можете ли вы использовать .fillna() для замены пропущенных значений контрольным показателем, чтобы сравнение работало?

Barmar 27.06.2024 19:29

Можете ли вы привести пример ввода, вызывающего эту проблему?

Barmar 27.06.2024 19:30

Я предполагаю, что это может сработать. Однако это занимает больше времени. В любом случае спасибо.

Joshua Roy 27.06.2024 20:09

@JoshuaRoy, твой результат неясен, почему у тебя 2 во второй строке? 1/2/1/1/2 только один, или вы тоже учитываете x=2?

mozway 27.06.2024 20:21
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
4
82
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать 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

Спасибо, это работает. Однако это занимает больше времени, чем требуется. Но спасибо за эту работу.

Joshua Roy 27.06.2024 20:03
Ответ принят как подходящий

Предполагая, что вы хотите подсчитать значения, а затем получить сумму по разным 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 секунды. Спасибо.

Joshua Roy 28.06.2024 02:44

@Джошуа, так какая версия тебе нужна?

mozway 28.06.2024 07:06

извините за поздний ответ... последний.

Joshua Roy 05.07.2024 07:48

Пытаться:

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

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