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

#python #pandas #dataframe #for-loop #list-comprehension

#python #pandas #фрейм данных #for-цикл #понимание списка

Вопрос:

У меня есть фрейм данных с идентификатором и некоторыми адресами электронной почты

 personid  sup1_email      sup2_email         sup3_email        sup4_email
1         evan.o@abc.com  jon.k@abc.com      kelm.q@abc.com    john.d@abc.com 
5         evan.o@abc.com  polly.u@abc.com    jim.e@ABC.COM     nan
11        jim.y@abc.com   manfred.a@abc.com  greg.s@Abc.com    adele.a@abc.com 
52        jim.y@abc.com   manfred.a@abc.com  greg.s@Abc.com    adele.a@abc.com
65        evan.o@abc.com  lenny.t@yahoo.com  john.s@abc.com    sally.j@ABC.com
89        dom.q@ABC.com   laurie.g@Abc.com   topher.u@abc.com  ross.k@qqpower.com
 

Я хотел бы найти строки, которые не соответствуют списку принятых значений электронной почты (т.е. Не ‘@abc.com ‘,’@ABC.COM ‘,’@Abc.com ‘). Что я хотел бы получить, так это

 personid  sup1_email      sup2_email         sup3_email        sup4_email
65        evan.o@abc.com  lenny.t@yahoo.com  john.s@abc.com    sally.j@ABC.com
89        dom.q@ABC.com   laurie.g@Abc.com   topher.u@abc.com  ross.k@qqpower.com
 

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

 #list down all the variations of accepted email domains
email_adds = ['@abc.com','@ABC.COM','@Abc.com']
#combine the variations of email addresses in the list
accepted_emails = '|'.join(email_adds)


not_accepted = df.loc[~df['sup1_email'].str.contains(accepted_emails, na=False)]
 

Мне было интересно, есть ли более эффективный способ сделать это с помощью цикла for. До сих пор мне удавалось показывать столбец, содержащий непринятое электронное письмо, но в нем не отображаются строки, содержащие непринятые электронные письма. Спасибо за любую помощь, которую я могу получить.

 sup_emails = df[['sup1_email','sup2_email', 'sup3_email', 'sup4_email']]

#for each sup column, check if the accepted email addresses are not in it
for col in sup_emails:
    if any(x not in col for x in accepted_emails):
        print(col)
 

Ответ №1:

Вы могли бы сделать:

 # list down all the variations of accepted email domains
email_adds = ['@abc.com','@ABC.COM','@Abc.com']

# combine the variations of email addresses in the list
accepted_emails = '|'.join(email_adds)

# create a single email column
melted = df.melt('personid')

# check the matching emails
mask = melted['value'].str.contains(accepted_emails, na=True)

# filter out the ones that do not match
mask = df['personid'].isin(melted.loc[~mask, 'personid'])

print(df[mask])
 

Вывод

    personid      sup1_email  ...        sup3_email          sup4_email
4        65  evan.o@abc.com  ...    john.s@abc.com     sally.j@ABC.com
5        89   dom.q@ABC.com  ...  topher.u@abc.com  ross.k@qqpower.com

[2 rows x 5 columns]
 

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

1. о, хм. Кажется, я не могу получить результат. Что означает расплавленный [‘value’] ?

2. @wjie08 Он должен содержать электронные письма

Ответ №2:

Одна идея:

 #list down all the variations of accepted email domains
email_adds = ['@abc.com','@ABC.COM','@Abc.com']
#combine the variations of email addresses in the list
accepted_emails = '|'.join(email_adds)

#columns for test
c = ['sup1_email','sup2_email', 'sup3_email', 'sup4_email']
#reshape and test all values, if `nan` pass `True`
m = df[c].stack(dropna=False).str.contains(accepted_emails, na=True).unstack().all(axis=1)

df = df[~m]
print (df)
   personid      sup1_email         sup2_email        sup3_email  
4        65  evan.o@abc.com  lenny.t@yahoo.com    john.s@abc.com   
5        89   dom.q@ABC.com   laurie.g@Abc.com  topher.u@abc.com   

           sup4_email  
4     sally.j@ABC.com  
5  ross.k@qqpower.com  
 

Ваше решение с генератором и any :

 c = ['sup1_email','sup2_email', 'sup3_email', 'sup4_email']

f = lambda y: any(x in y for x in email_adds) if isinstance(y, str) else True
df = df[~df[c].applymap(f).all(axis=1)]
print (df)
   personid      sup1_email         sup2_email        sup3_email  
4        65  evan.o@abc.com  lenny.t@yahoo.com    john.s@abc.com   
5        89   dom.q@ABC.com   laurie.g@Abc.com  topher.u@abc.com   

           sup4_email  
4     sally.j@ABC.com  
5  ross.k@qqpower.com  
 

Ответ №3:

Давайте попробуем проверить, чтобы увидеть, что символы после @ являются или ABC или abc или Abc во всех столбцах. Конечно, нам может потребоваться временно отфильтровать PersonID .После проверки измените результат, используя ~ и маскируя их

 df[-(df.iloc[:,1:].apply(lambda x: x.str.contains('(@(?=ABC|abc|Abc))').all(), axis=1))]



 personid      sup1_email         sup2_email        sup3_email  
4      65.0  evan.o@abc.com  lenny.t@yahoo.com    john.s@abc.com   
5      89.0   dom.q@ABC.com   laurie.g@Abc.com  topher.u@abc.com   

           sup4_email  
4     sally.j@ABC.com  
5  ross.k@qqpower.com