#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