Как проверить, есть ли в фрейме данных строка с одинаковыми комбинациями значений?

#python #dataframe

#python #фрейм данных

Вопрос:

У меня есть фрейм данных, и я хочу создать новый столбец на основе других строк фрейма данных. Мой фрейм данных выглядит так

     MitarbeiterID   ProjektID   Jahr    Monat   Week    mean    freq    last
0       583          83224      2020    1        2      3.875    4       0
1       373          17364      2020    1        3      5.00     0       4
2       923          19234      2020    1        4      5.00     3       3
3       643          17364      2020    1        3      4.00     2       2

 

Теперь я хочу проверить, равна ли частота строки нулю, тогда я проверю, есть ли другая строка с тем же идентификатором проекта и годом недели, где частота не равна 0. Если это правда, я хочу новый столбец «other», который имеет значение 1 и 0 else.
Итак, вывод должен быть

     MitarbeiterID   ProjektID   Jahr    Monat   Week    mean    freq    last other
0       583          83224      2020    1        2      3.875    4       0     0
1       373          17364      2020    1        3      5.00     0       4     1
2       923          19234      2020    1        4      5.00     3       3     0
3       643          17364      2020    1        3      4.00     2       2     0

 

На этот раз у меня нет подхода, кто-нибудь может помочь?
Спасибо!

Комментарии:

1. Почему other в строке 3 0, а не 1?

2. @Timus Я не уверен, что OP мог бы уточнить, но мне кажется, что это продиктовано столбцом freq, так как в строке 3 freq не равен 0, я думаю, OP хочет пометить other как 1, где freq равно 0??

3. @Timus Да, потому что частота в строке 3 равна 3, а не 0

Ответ №1:

Следующее решение проверяет, выполняются ли требуемые условия.

 import io
import pandas as pd
 

Данные

 df = pd.read_csv(io.StringIO("""
MitarbeiterID   ProjektID   Jahr    Monat   Week    mean    freq    last
0       583          83224      2020    1        2      3.875    4       0
1       373          17364      2020    1        3      5.00     0       4
2       923          19234      2020    1        4      5.00     3       3
3       643          17364      2020    1        3      4.00     2       2
"""), sep="ss ", engine="python")
 

Создайте столбец other со всеми нулевыми значениями.

 df['other'] = 0
 

Если ProjektID, Jahr, Week дублируются, и любое из значений Freq больше нуля, то дублирующиеся строки (keep=False, чтобы также захватить исходную дублированную строку) и где Freq равно нулю, будут иметь значение Other, заполненное 1. Измените any() на all() , если вам нужно, чтобы все значения были больше нуля.

 if (df.loc[df[['ProjektID','Jahr', 'Week']].duplicated(), 'freq'] > 0).any(): df.loc[(df[['ProjektID','Jahr', 'Week']].duplicated(keep=False)) amp; (df['freq'] == 0), ['other']] = 1
else: print("Other stays zero")
 

Вывод:

введите описание изображения здесь

Ответ №2:

Я думаю, что лучший способ решить эту проблему — не использовать pandas слишком часто 🙂 преобразование объектов в наборы и кортежи должно сделать это достаточно быстро.

Идея состоит в том, чтобы составить словарь всех троек (ProjektID, Jahr, Week) , которые появляются в наборе freq != 0 данных, а затем проверить все строки с freq == 0 помощью, принадлежит ли их тройка этому словарю или нет. В коде я создаю фиктивный набор данных с помощью:

 x = pd.DataFrame(np.random.randint(0, 2, (8, 4)), columns=['id', 'year', 'week', 'freq'])
 

который в моем случае случайным образом дал:

 >>> x
   id  year  week  freq
0   1     0     0     0
1   0     0     0     1
2   0     1     0     1
3   0     0     1     0
4   0     1     0     0
5   1     0     0     1
6   0     0     1     1
7   0     1     1     0
 

Теперь нам нужны тройки только там, где freq != 0 , поэтому мы используем

 x1 = x.loc[x['freq'] != 0]
triplets = {tuple(row) for row in x1[['id', 'year', 'week']].values}
 

Обратите внимание, что я использую x1.values , который является не фреймом данных pandas, а скорее массивом numpy; так что каждая строка в нем теперь может быть преобразована в кортеж. Это необходимо, потому что строки фрейма данных или даже numpy-массив или списки являются изменяемыми объектами и в противном случае не могут быть хэшированы в словаре. Использование набора вместо, например, списка (который не имеет этого ограничения) предназначено для повышения эффективности.

Далее мы определяем логическую переменную, которая имеет значение True, если триплет (идентификатор, год, неделя) принадлежит к вышеуказанному набору:

 belongs = x[['id', 'year', 'week']].apply(lambda x: tuple(x) in triplets, axis=1)
 

Мы в основном закончили, это еще один нужный вам столбец, за исключением того, что также необходимо принудительно использовать freq == 0:

 x['other'] = np.logical_and(belongs, x['freq'] == 0).astype(int)
 

(в конечном .astype(int) итоге он должен иметь значения 0 и 1, как вы просили, вместо False и True). Конечный результат в моем случае:

 >>> x
   id  year  week  freq  other
0   1     0     0     0      1
1   0     0     0     1      0
2   0     1     0     1      0
3   0     0     1     0      1
4   0     1     0     0      1
5   1     0     0     1      0
6   0     0     1     1      0
7   0     1     1     0      0
 

Комментарии:

1. Спасибо! Я думаю, что есть что-то, что исключает само себя. Я только что получил нули в своем столбце other, поэтому никогда не бывает freq = 0 и belongs=True . Я пробовал другие случаи, которые подтверждают это. Но я не понимаю..

Ответ №3:

Похоже, я опоздал…:

 df.set_index(['ProjektID', 'Jahr', 'Week'], drop=True, inplace=True)
df['other'] = 0
df.other.mask(df.freq == 0,
              df.freq[df.freq == 0].index.isin(df.freq[df.freq != 0].index),
              inplace=True)
df.other = df.other.astype('int')
df.reset_index(drop=False, inplace=True)
 

Комментарии:

1. В SO никогда не бывает слишком поздно. Хорошее решение!