Как выполнить итерацию строки и сравнить друг с другом?

#python #python-3.x #pandas #dataframe #iteration

Вопрос:

Например, если имя ‘Mark’ повторяется и если значение ‘Age’ или ‘Gender’ отличается по сравнению с другими строками, которые содержат ‘Mark’, то такие столбцы должны быть удалены.

Код для создания фрейма данных:

 df = pd.DataFrame({'Name' : ['Mark', 'Mark', 'Mark', 'Mark', 'Mark', 'Nick', 'Nick', 'John', 'Sunny', 'Sunny'], 
                  'Age' : ['22', '22', '25', '25', '17', '20', '20', '17', '23', '23'],
                  'Gender' : ['F', 'F', 'F', 'F', 'F', 'F', 'F', 'M', 'M', 'M']})
 

Фрейм данных приведен ниже:

        Name   Age   Gender
0      Mark   22    F
1      Mark   22    F
2      Mark   25    F
3      Mark   25    F
4      Mark   17    F
5      Nick   20    F
6      Nick   20    F
7      John   17    M
8      Sunny  23    M
9      Sunny  23    M
 

Ожидаемый результат:

        Name   Age   Gender
0      Nick   20    F
1      Nick   20    F
2      John   17    M
3      Sunny  23    M
4      Sunny  23    M
 

Например, в 1-й и 2-й строках имя — Марк, возраст — 22, а пол — F. В то время как в 3-й строке имя — Mark, возраст — 25, а пол — F, мы видим, что возраст имеет несколько значений, тогда нам нужно удалить строки, содержащие Mark

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

1. Какие проблемы у вас возникают при выполнении этого самостоятельно? На самом деле это не проблема программирования

2. Я не согласен, @Laif. Это можно сделать с pandas помощью группировки и унификации, но это не очевидно.

3. @TimRoberts Это вполне возможно, я говорю, что вопрос, похоже, касается математического метода или инструмента для выполнения операций, а не проблемы, с которой OP сталкивается с их программой.

Ответ №1:

Хорошо, это сложно, поэтому я объясню каждую строку.

 import pandas as pd

df = pd.DataFrame({'Name' : ['Mark', 'Mark', 'Mark', 'Mark', 'Mark', 'Nick', 'Nick', 'John', 'Sunny', 'Sunny'], 
                  'Age' : ['22', '22', '25', '25', '17', '20', '20', '17', '23', '23'],
                  'Gender' : ['F', 'F', 'F', 'F', 'F', 'F', 'F', 'M', 'M', 'M']})

print(df)

# First, drop all the rows that are exact duplicates of each other.

df1 = df.drop_duplicates()
print(df1)

# If any rows that remain have a duplicate name, those need to go.

df2 = df1.duplicated('Name')
print(df2)

# Go build a DF with just the names that were duplicated.

df3 = df1[df2]['Name']
print(df3)

# Find all the rows in the original df that have a name in this list,
# invert that set, and the result is what we want.

df4 = df[~df.Name.isin(df3)]
print(df4)
 

Несколько из этих операций можно объединить, чтобы сделать однострочный, но я думаю, что это делает его более понятным. Я добавил пустые строки для разделения фреймов данных в выходных данных.

Вывод:

     Name Age Gender
0   Mark  22      F
1   Mark  22      F
2   Mark  25      F
3   Mark  25      F
4   Mark  17      F
5   Nick  20      F
6   Nick  20      F
7   John  17      M
8  Sunny  23      M
9  Sunny  23      M

    Name Age Gender
0   Mark  22      F
2   Mark  25      F
4   Mark  17      F
5   Nick  20      F
7   John  17      M
8  Sunny  23      M

0    False
2     True
4     True
5    False
7    False
8    False
dtype: bool

2    Mark
4    Mark
Name: Name, dtype: object

    Name Age Gender
5   Nick  20      F
6   Nick  20      F
7   John  17      M
8  Sunny  23      M
9  Sunny  23      M
 

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

1. Спасибо, Тим!! Это работает.

Ответ №2:

Однострочный ответ:

 df[df.Name.isin((s:=df.drop_duplicates().groupby(['Name']).size().eq(1)).index[s])]
 

результаты в:

     Name Age Gender
5   Nick  20      F
6   Nick  20      F
7   John  17      M
8  Sunny  23      M
9  Sunny  23      M
 

Возможно, стоит отметить: оператор walrus по-прежнему выделяет память для переменной, s которую сборщик мусора Python в конечном итоге удалит. Если вам понадобится Series представление, имеет ли какой Name -либо из них разные строки, как показано ниже, тогда стоило бы явно назначить его.

 >>> s
Name
John      True
Mark     False
Nick      True
Sunny     True
dtype: bool
 

в любом другом месте ваших вычислений, вероятно, стоит явно присвоить его переменной, на которую вы поддерживаете ссылку.


Объяснение:

 # from the OG df
df[
   # boolean mask if the name is in
   df.Name.isin(
                # walrus-operator to temporarily hold result
                # drop the duplicate rows (duplicates name   age   gender)
                (s:=df.drop_duplicates()
                       # group on Name
                       .groupby(['Name'])
                       # after dropping duplicates, there's only one record
                       # i.e. no different age/gender records for same name
                       .size().eq(1))
                # mask on names where no-duplicates is true, drop names where false
                ).index[s]
               # pass as series to `df.Name.isin`
               )
  ]
 

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

1. Когда я попытался запустить этот код df[df.Name.isin(s:=(df.drop_duplicates().groupby([‘Name’]).size().eq(1)).index[s])], он возвращал ошибку типа name ‘s’не определено

2. @NithinReddy какую версию Python вы используете? Оператор walrus ( s:=... ) является новым для Python 3.8

3. Я использую python 3.8.5, но его возвращаемые ‘s’ не определены

4. Есть ли альтернатива?

5. исправлено @NithinReddy; не ( в том месте.