在 R 中:按主题行搜索所有电子邮件,从正文中提取逗号分隔值,然后将值保存在数据框中
In R: Search all emails by subject line, pull comma-separate values from body, then save values in a dataframe
每天,我都会收到一封电子邮件,其中包含特定日期售出的水果数量。邮件结构如下:
Date of report:,04-JAN-2022
Time report produced:,5-JAN-2022 02:04
Apples,6
Pears,1
Lemons,4
Oranges,2
Grapes,7
Grapefruit,2
我正在尝试在 R 中构建一些代码来搜索我的电子邮件,找到所有具有特定主题的电子邮件,遍历每封电子邮件以找到我正在寻找的变量,获取值并将它们放置在日期列中包含“报告日期”的数据框中。
在社区人士的帮助下,我在 Python 中取得了预期的结果。然而,随着我的项目的发展,我现在需要尽可能在 R 中实现相同的结果。
不幸的是,我对 R 很陌生,因此如果有人对如何推进它有任何建议,我将不胜感激。
感兴趣的朋友,我的 Python 代码如下:
#PREP THE STUFF
Fruit_1 = "Apples"
Fruit_2 = "Pears"
searchf = [
Fruit_1,
Fruit_2
]
#DEF THE STUFF
def get_report_vals(report, searches):
dct = {}
for line in report:
term, *value = line
if term.casefold().startswith('date'):
dct['date'] = pd.to_datetime(value[0])
elif term in searches:
dct[term] = float(value[0])
if len(dct.keys()) != len(searches):
dct.update({x: None for x in searches if x not in dct})
return dct
#DO THE STUFF
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
messages.Sort("[ReceivedTime]", True)
results = []
for message in messages:
if message.subject == 'FRUIT QUANTITIES':
if Fruit_1 in message.body and Fruit_2 in message.body:
data = [line.strip().split(",") for line in message.body.split('\n')]
results.append(get_report_vals(data, searchf))
else:
pass
fruit_vals = pd.DataFrame(results)
fruit_vals.columns = map(str.upper, fruit_vals.columns)
我可能以错误的方式解决这个问题,但我正在尝试使用我在 Python 中采取的步骤在 R 中实现相同的结果。例如,我创建了一些变量来保存我正在搜索的水果销售,然后我创建一个向量来存储可搜索的内容,然后当我创建一个等效的 'get_vals' 函数时,我创建一个空向量。
library(RDCOMClient)
Fruit_1 <- "Apples"
Fruit_2 <- "Pears"
##Create vector to store searchables
searchf <- c(Fruit_1, Fruit_2)
## create object for outlook
OutApp <- COMCreate("Outlook.Application")
outlookNameSpace = OutApp$GetNameSpace("MAPI")
search <- OutApp$AdvancedSearch("Inbox", "urn:schemas:httpmail:subject = 'FRUIT QUANTITIES'")
inbox <- outlookNameSpace$Folders(6)$Folders("Inbox")
vec <- c()
for (x in emails)
{
subject <- emails(i)$Subject(1)
if (grepl(search, subject)[1])
{
text <- emails(i)$Body()
print(text)
break
}
}
可能这不是您期望得到的答案,但我必须在这里声明,以帮助其他读者将来避免此类错误。
很遗憾,您的 Python 代码不是 well-written。例如,我注意到以下代码,您在其中遍历文件夹中的所有项目并检查 Subject
和邮件正文中的关键字:
for message in messages:
if message.subject == 'FRUIT QUANTITIES':
if Fruit_1 in message.body and Fruit_2 in message.body:
您需要改用项目 class 的 Find
/FindNext
或 Restrict
方法。因此,您不需要遍历文件夹中的所有项目。相反,您只会得到符合您条件的物品。在以下文章中阅读有关这些方法的更多信息:
- How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
- How To: Use Restrict method to retrieve Outlook mail items from a folder
您可以将所有搜索条件合并到一个查询中。因此,您只需要遍历找到的项目并提取数据。
您可能还会发现 AdvancedSearch 方法很有帮助。在 Outlook 中使用 AdvancedSearch
方法的主要好处是:
- 搜索在另一个线程中执行。您不需要手动 运行 另一个线程,因为
AdvancedSearch
方法 运行 它会自动在后台运行。
- 可以在任何位置(即超出某个文件夹的范围)搜索任何项目类型:邮件、约会、日历、便笺等。
Restrict
和 Find
/FindNext
方法可以应用于特定的 Items
集合(请参阅 [=22= 的 Items
属性 ] class 在 Outlook 中)。
- 完全支持 DASL 查询(自定义属性也可用于搜索)。您可以在 MSDN 的 Filtering 文章中阅读更多相关信息。为了提高搜索性能,如果为商店启用了即时搜索,则可以使用即时搜索关键字(请参阅
Store
class 的 IsInstantSearchEnabled
属性)。
- 您可以随时使用
Search
class 的 Stop
方法停止搜索过程。
有关详细信息,请参阅 Advanced search in Outlook programmatically: C#, VB.NET。
read.table
可能是 get_report_vals
的良好开端。
下面的代码将结果输出为列表,异常处理仍然需要实现:
report <- "
Date of report:,04-JAN-2022
Apples,6
Pears,1
Lemons,4
Oranges,2
Grapes,7
Grapefruit,2
"
get_report_vals <- function(report,searches) {
data <- read.table(text=report,sep=",")
colnames(data) <- c('key','value')
# find date
date <- data[grepl("date",data$key,ignore.case=T),"value"]
# transform dataframe to list
lst <- split(data$value,data$key)
# output result as list
c(list(date=date),lst[searches])
}
get_report_vals(report,c('Lemons','Oranges'))
$date
[1] "04-JAN-2022"
$Lemons
[1] "4"
$Oranges
[1] "2"
然后可以使用 rbind
:
将各种报告的结果连接成 data.frame
rbind(get_report_vals(report,c('Lemons','Oranges')),get_report_vals(report,c('Lemons','Oranges')))
date Lemons Oranges
[1,] "04-JAN-2022" "4" "2"
[2,] "04-JAN-2022" "4" "2"
代码现在可以按预期运行。函数的编写与推荐的有很大不同:
get_vals <- function(email) {
body <- email$body()
date <- str_extract(body, "\d{2}-[:alpha:]{3}-\d{4}") %>%
as.character()
data <- read.table(text = body, sep = ",", skip = 9, strip.white = T) %>%
row_to_names(1) %>%
mutate("Date" = date)
return(data)
}
此外,我还写了这个来将行绑定在一起:
info <- sapply(results, get_vals, simplify = F) %>%
bind_rows()
每天,我都会收到一封电子邮件,其中包含特定日期售出的水果数量。邮件结构如下:
Date of report:,04-JAN-2022
Time report produced:,5-JAN-2022 02:04
Apples,6
Pears,1
Lemons,4
Oranges,2
Grapes,7
Grapefruit,2
我正在尝试在 R 中构建一些代码来搜索我的电子邮件,找到所有具有特定主题的电子邮件,遍历每封电子邮件以找到我正在寻找的变量,获取值并将它们放置在日期列中包含“报告日期”的数据框中。
在社区人士的帮助下,我在 Python 中取得了预期的结果。然而,随着我的项目的发展,我现在需要尽可能在 R 中实现相同的结果。
不幸的是,我对 R 很陌生,因此如果有人对如何推进它有任何建议,我将不胜感激。
感兴趣的朋友,我的 Python 代码如下:
#PREP THE STUFF
Fruit_1 = "Apples"
Fruit_2 = "Pears"
searchf = [
Fruit_1,
Fruit_2
]
#DEF THE STUFF
def get_report_vals(report, searches):
dct = {}
for line in report:
term, *value = line
if term.casefold().startswith('date'):
dct['date'] = pd.to_datetime(value[0])
elif term in searches:
dct[term] = float(value[0])
if len(dct.keys()) != len(searches):
dct.update({x: None for x in searches if x not in dct})
return dct
#DO THE STUFF
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
messages.Sort("[ReceivedTime]", True)
results = []
for message in messages:
if message.subject == 'FRUIT QUANTITIES':
if Fruit_1 in message.body and Fruit_2 in message.body:
data = [line.strip().split(",") for line in message.body.split('\n')]
results.append(get_report_vals(data, searchf))
else:
pass
fruit_vals = pd.DataFrame(results)
fruit_vals.columns = map(str.upper, fruit_vals.columns)
我可能以错误的方式解决这个问题,但我正在尝试使用我在 Python 中采取的步骤在 R 中实现相同的结果。例如,我创建了一些变量来保存我正在搜索的水果销售,然后我创建一个向量来存储可搜索的内容,然后当我创建一个等效的 'get_vals' 函数时,我创建一个空向量。
library(RDCOMClient)
Fruit_1 <- "Apples"
Fruit_2 <- "Pears"
##Create vector to store searchables
searchf <- c(Fruit_1, Fruit_2)
## create object for outlook
OutApp <- COMCreate("Outlook.Application")
outlookNameSpace = OutApp$GetNameSpace("MAPI")
search <- OutApp$AdvancedSearch("Inbox", "urn:schemas:httpmail:subject = 'FRUIT QUANTITIES'")
inbox <- outlookNameSpace$Folders(6)$Folders("Inbox")
vec <- c()
for (x in emails)
{
subject <- emails(i)$Subject(1)
if (grepl(search, subject)[1])
{
text <- emails(i)$Body()
print(text)
break
}
}
可能这不是您期望得到的答案,但我必须在这里声明,以帮助其他读者将来避免此类错误。
很遗憾,您的 Python 代码不是 well-written。例如,我注意到以下代码,您在其中遍历文件夹中的所有项目并检查 Subject
和邮件正文中的关键字:
for message in messages:
if message.subject == 'FRUIT QUANTITIES':
if Fruit_1 in message.body and Fruit_2 in message.body:
您需要改用项目 class 的 Find
/FindNext
或 Restrict
方法。因此,您不需要遍历文件夹中的所有项目。相反,您只会得到符合您条件的物品。在以下文章中阅读有关这些方法的更多信息:
- How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
- How To: Use Restrict method to retrieve Outlook mail items from a folder
您可以将所有搜索条件合并到一个查询中。因此,您只需要遍历找到的项目并提取数据。
您可能还会发现 AdvancedSearch 方法很有帮助。在 Outlook 中使用 AdvancedSearch
方法的主要好处是:
- 搜索在另一个线程中执行。您不需要手动 运行 另一个线程,因为
AdvancedSearch
方法 运行 它会自动在后台运行。 - 可以在任何位置(即超出某个文件夹的范围)搜索任何项目类型:邮件、约会、日历、便笺等。
Restrict
和Find
/FindNext
方法可以应用于特定的Items
集合(请参阅 [=22= 的Items
属性 ] class 在 Outlook 中)。 - 完全支持 DASL 查询(自定义属性也可用于搜索)。您可以在 MSDN 的 Filtering 文章中阅读更多相关信息。为了提高搜索性能,如果为商店启用了即时搜索,则可以使用即时搜索关键字(请参阅
Store
class 的IsInstantSearchEnabled
属性)。 - 您可以随时使用
Search
class 的Stop
方法停止搜索过程。
有关详细信息,请参阅 Advanced search in Outlook programmatically: C#, VB.NET。
read.table
可能是 get_report_vals
的良好开端。
下面的代码将结果输出为列表,异常处理仍然需要实现:
report <- "
Date of report:,04-JAN-2022
Apples,6
Pears,1
Lemons,4
Oranges,2
Grapes,7
Grapefruit,2
"
get_report_vals <- function(report,searches) {
data <- read.table(text=report,sep=",")
colnames(data) <- c('key','value')
# find date
date <- data[grepl("date",data$key,ignore.case=T),"value"]
# transform dataframe to list
lst <- split(data$value,data$key)
# output result as list
c(list(date=date),lst[searches])
}
get_report_vals(report,c('Lemons','Oranges'))
$date
[1] "04-JAN-2022"
$Lemons
[1] "4"
$Oranges
[1] "2"
然后可以使用 rbind
:
data.frame
rbind(get_report_vals(report,c('Lemons','Oranges')),get_report_vals(report,c('Lemons','Oranges')))
date Lemons Oranges
[1,] "04-JAN-2022" "4" "2"
[2,] "04-JAN-2022" "4" "2"
代码现在可以按预期运行。函数的编写与推荐的有很大不同:
get_vals <- function(email) {
body <- email$body()
date <- str_extract(body, "\d{2}-[:alpha:]{3}-\d{4}") %>%
as.character()
data <- read.table(text = body, sep = ",", skip = 9, strip.white = T) %>%
row_to_names(1) %>%
mutate("Date" = date)
return(data)
}
此外,我还写了这个来将行绑定在一起:
info <- sapply(results, get_vals, simplify = F) %>%
bind_rows()