如何在两列列表中获取唯一的电子邮件,其中值可以有两个(或更多!)单独的值
How to get unique emails in a two column list where values can have two (or more!) separate values
这是我几周前问过的一个类似问题的转贴,我认为我能够做到这一点,但我的表现会付出巨大的(阅读:无法维持的)代价。那里有一个英雄海报对我有所帮助,我在 his/her 方向重新发布。我的代码:
Donors = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Donors Q1 2021 R12.csv",
usecols=["Email Address"], na_values= True)
Activists = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Activists Q1 2021 R12.csv",
usecols=["Email"], na_filter= True)
Low_Level_Activists = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Low Level Activists Q1 2021 R12.csv",
usecols=["Email"], na_filter= True)
Ambassadors = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Student Ambassadors Q1 2021.csv",
usecols=["Email Address"], na_filter= True)
Volunteers = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Volunteers Q1 2021 R12.csv",
usecols=["Email Address"], na_filter= True)
Donors['Value'] = "Donors"
Activists['Value'] = "Activists"
Low_Level_Activists['Value'] = "Low_Level_Activists"
Ambassadors['Value'] = "Ambassadors"
Volunteers['Value'] = "Volunteers"
Advocates['Value'] = 'Followers'
S1= pd.concat([Donors,Activists,Low_Level_Activists,Ambassadors,Volunteers,Advocates], ignore_index= True)
S1['Handle'] = S1['Email Address'].where(S1['Email Address'].notnull(), S1['Email'])
S1= S1.drop(['Email','Email Address'], axis = 1)
print(S1['Handle'].count()) #checks full count
现在是困难的部分 - 电子邮件地址可以是捐助者和活动家、活动家和志愿者,所有五个价值观等等,但大多数情况下它是一个电子邮件一个值。 我需要获取一个 Dataframe(或列表或字典),我在其中捕获了最早导入的唯一电子邮件地址 class。如果电子邮件是捐助者和志愿者,那么它就是捐助者。如果是 Activist 和 Low Level Activist 那么 Activist 等等。
我正在寻找具有每个值组以及电子邮件的唯一计数的输出。此代码:
pd.Series({value: len(group.Handle.unique()) for value, group in S1.groupby('Value')})
可以解决问题 (h/t Jan W.),但它不会根据最早的“值”压缩电子邮件计数。帮助将不胜感激,因为我已经在这里工作了很多天。此集合中有 1,700 个实例,其中一封电子邮件存在于 167,000 个电子邮件地址中的两个或更多位置。我似乎无法绕过使用 for 循环然后切换回 pandas 系列等。如果我能获得有效的代码再次解决这个问题,将不胜感激!
示例数据(再次 h/t Jan W):
fa = pd.DataFrame([['paul@mail.com', 'Donors'], ['max@mail.com', 'Donors']], columns=['Handle', 'Value'])
fb = pd.DataFrame([['paul@mail.com', 'Activists'], ['annie@mail.com', 'Activists']], columns=['Handle', 'Value'])
S1 = pd.concat([fa, fb])
结合pandas groupby
和sort_values
方法将使运行 一切变得相当快。下面的代码片段是一个示例实现。
首先生成约 100K 行的随机数据(这有点扭曲但与您的实际问题无关):
from itertools import combinations
import string
import numpy as np
import pandas as pd
emails = [
f"{''.join(chars)}@{domain}"
for chars in combinations(string.ascii_lowercase, 4)
for domain in ["example.com", "domain.org"]
]
roles = [
"Donors",
"Activists",
"Low_Level_Activists",
"Ambassadors",
"Ambassadors",
"Ambassadors",
]
n = 100_000
df = pd.DataFrame(
{
"Handle": np.random.choice(emails, size=n),
"Value": np.random.choice(roles, size=n),
}
).drop_duplicates()
现在 df
看起来像:
Handle Value
0 agnt@domain.org Ambassadors
1 fmox@domain.org Low_Level_Activists
2 cmny@domain.org Low_Level_Activists
3 fgpq@domain.org Ambassadors
4 hikw@example.com Low_Level_Activists
... ... ...
99993 ejvz@domain.org Activists
99994 aehr@domain.org Ambassadors
99995 efjy@example.com Ambassadors
99997 aeow@example.com Ambassadors
99999 aetw@domain.org Donors
[62609 rows x 2 columns]
然后为每封电子邮件获取它所属的第一个角色:
df = df.sort_values("Value").groupby("Handle", as_index=False)["Value"].first()
Handle Value
0 abcd@domain.org Activists
1 abcd@example.com Donors
2 abce@domain.org Ambassadors
3 abce@example.com Activists
4 abcf@domain.org Ambassadors
... ... ...
28848 vwyz@example.com Activists
28849 vxyz@domain.org Ambassadors
28850 vxyz@example.com Ambassadors
28851 wxyz@domain.org Ambassadors
28852 wxyz@example.com Activists
[28853 rows x 2 columns]
最后计算每个角色的电子邮件数量:
df = df.groupby("Value")["Handle"].nunique()
Value
Activists 12765
Ambassadors 13987
Donors 1374
Low_Level_Activists 727
Name: Handle, dtype: int64
一旦定义了 df
,上面的两个计算在我的笔记本电脑上仅需 运行 ~100ms。
这是我几周前问过的一个类似问题的转贴,我认为我能够做到这一点,但我的表现会付出巨大的(阅读:无法维持的)代价。那里有一个英雄海报对我有所帮助,我在 his/her 方向重新发布。我的代码:
Donors = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Donors Q1 2021 R12.csv",
usecols=["Email Address"], na_values= True)
Activists = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Activists Q1 2021 R12.csv",
usecols=["Email"], na_filter= True)
Low_Level_Activists = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Low Level Activists Q1 2021 R12.csv",
usecols=["Email"], na_filter= True)
Ambassadors = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Student Ambassadors Q1 2021.csv",
usecols=["Email Address"], na_filter= True)
Volunteers = pd.read_csv(r"C:\Users\am\Desktop\email parsing\Q1 2021\Volunteers Q1 2021 R12.csv",
usecols=["Email Address"], na_filter= True)
Donors['Value'] = "Donors"
Activists['Value'] = "Activists"
Low_Level_Activists['Value'] = "Low_Level_Activists"
Ambassadors['Value'] = "Ambassadors"
Volunteers['Value'] = "Volunteers"
Advocates['Value'] = 'Followers'
S1= pd.concat([Donors,Activists,Low_Level_Activists,Ambassadors,Volunteers,Advocates], ignore_index= True)
S1['Handle'] = S1['Email Address'].where(S1['Email Address'].notnull(), S1['Email'])
S1= S1.drop(['Email','Email Address'], axis = 1)
print(S1['Handle'].count()) #checks full count
现在是困难的部分 - 电子邮件地址可以是捐助者和活动家、活动家和志愿者,所有五个价值观等等,但大多数情况下它是一个电子邮件一个值。 我需要获取一个 Dataframe(或列表或字典),我在其中捕获了最早导入的唯一电子邮件地址 class。如果电子邮件是捐助者和志愿者,那么它就是捐助者。如果是 Activist 和 Low Level Activist 那么 Activist 等等。
我正在寻找具有每个值组以及电子邮件的唯一计数的输出。此代码:
pd.Series({value: len(group.Handle.unique()) for value, group in S1.groupby('Value')})
可以解决问题 (h/t Jan W.),但它不会根据最早的“值”压缩电子邮件计数。帮助将不胜感激,因为我已经在这里工作了很多天。此集合中有 1,700 个实例,其中一封电子邮件存在于 167,000 个电子邮件地址中的两个或更多位置。我似乎无法绕过使用 for 循环然后切换回 pandas 系列等。如果我能获得有效的代码再次解决这个问题,将不胜感激!
示例数据(再次 h/t Jan W):
fa = pd.DataFrame([['paul@mail.com', 'Donors'], ['max@mail.com', 'Donors']], columns=['Handle', 'Value'])
fb = pd.DataFrame([['paul@mail.com', 'Activists'], ['annie@mail.com', 'Activists']], columns=['Handle', 'Value'])
S1 = pd.concat([fa, fb])
结合pandas groupby
和sort_values
方法将使运行 一切变得相当快。下面的代码片段是一个示例实现。
首先生成约 100K 行的随机数据(这有点扭曲但与您的实际问题无关):
from itertools import combinations
import string
import numpy as np
import pandas as pd
emails = [
f"{''.join(chars)}@{domain}"
for chars in combinations(string.ascii_lowercase, 4)
for domain in ["example.com", "domain.org"]
]
roles = [
"Donors",
"Activists",
"Low_Level_Activists",
"Ambassadors",
"Ambassadors",
"Ambassadors",
]
n = 100_000
df = pd.DataFrame(
{
"Handle": np.random.choice(emails, size=n),
"Value": np.random.choice(roles, size=n),
}
).drop_duplicates()
现在 df
看起来像:
Handle Value
0 agnt@domain.org Ambassadors
1 fmox@domain.org Low_Level_Activists
2 cmny@domain.org Low_Level_Activists
3 fgpq@domain.org Ambassadors
4 hikw@example.com Low_Level_Activists
... ... ...
99993 ejvz@domain.org Activists
99994 aehr@domain.org Ambassadors
99995 efjy@example.com Ambassadors
99997 aeow@example.com Ambassadors
99999 aetw@domain.org Donors
[62609 rows x 2 columns]
然后为每封电子邮件获取它所属的第一个角色:
df = df.sort_values("Value").groupby("Handle", as_index=False)["Value"].first()
Handle Value
0 abcd@domain.org Activists
1 abcd@example.com Donors
2 abce@domain.org Ambassadors
3 abce@example.com Activists
4 abcf@domain.org Ambassadors
... ... ...
28848 vwyz@example.com Activists
28849 vxyz@domain.org Ambassadors
28850 vxyz@example.com Ambassadors
28851 wxyz@domain.org Ambassadors
28852 wxyz@example.com Activists
[28853 rows x 2 columns]
最后计算每个角色的电子邮件数量:
df = df.groupby("Value")["Handle"].nunique()
Value
Activists 12765
Ambassadors 13987
Donors 1374
Low_Level_Activists 727
Name: Handle, dtype: int64
一旦定义了 df
,上面的两个计算在我的笔记本电脑上仅需 运行 ~100ms。