【问题标题】:Converting VBA script to Python Script将 VBA 脚本转换为 Python 脚本
【发布时间】:2021-04-15 17:30:05
【问题描述】:

我正在尝试将一些 VBA 脚本转换为 python 脚本,但我在试图弄清楚一些事情时遇到了麻烦,因为结果似乎与 excel 文件给出的不同。 所以我有一个这样的示例数据框:

|Name    |  A_Date    |
_______________________
|RAHEAL  |  04/30/2020|
|GIFTY   |  05/31/2020|
||ERIC   |  03/16/2020|
|PETER   |  05/01/2020|
|EMMANUEL|  12/15/2019| 
|BABA    |  05/23/2020|

我想达到这个结果(VBA 脚本结果):

|Name    |  A_Date    | Sold
__________________________________
|RAHEAL  |  04/30/2020| No
|GIFTY   |  05/31/2020| Yes
||ERIC   |  03/16/2020| No
|PETER   |  05/01/2020| Yes
|EMMANUEL|  12/15/2019| No
|BABA    |  05/23/2020| Yes

通过转换此 VBA 脚本:

Range("C2").Select
    Selection.Value = _
        "=IF(RC[-1]>=(INT(R2C2)-DAY(INT(R2C2))+1),""Yes"",""No"")"
    Selection.AutoFill Destination:=Range("C2", "C" & Cells(Rows.Count, 1).End(xlUp).Row)
    Range("C1").Value = "Sold"
    ActiveSheet.Columns("C").Copy
    ActiveSheet.Columns("C").PasteSpecial xlPasteValues

只需:=IF(B2>=(INT($B$2)-DAY(INT($B$2))+1),"Yes","No")

到这个 Python 脚本:

sales['Sold']=np.where(sales['A_Date']>=(sales['A_Date'] - pd.to_timedelta(sales.A_Date.dt.day, unit='d'))+ timedelta(days=1),'Yes','No') 

但我一直在回答“是”……谁能帮我找出我可能犯了某种错误的地方

【问题讨论】:

  • 为什么不试试自动转换器?那是我先尝试的,然后看看它是否有效,也许可以清理代码
  • @wamster 你能推荐一些好的吗?
  • 对不起,我没有这方面的经验,但快速谷歌搜索显示this 可能适合您的账单。这里的主要内容是确保它最近已更新,并且已经更新了。否则,如果它像 5 岁一样,它可能会转换为 python2
  • 您的 VBA 正在引用 L 列,但您的数据框最初只有 2 列。而且您的 VBA 公式与您的 Excel 公式不匹配。如果您的 Excel 公式是正确的,那么您的第一个期望输出 Sold 值是错误的。

标签: python excel vba dataframe


【解决方案1】:

非常尴尬,我没有看到 buran 给出的简单优雅的解决方案 +。我做了更多的直译。

first_date.toordinal() - 693594 是您的初始日期的整数日期值,current_date.toordinal() - 693594 是日期列的当前迭代的整数日期值。我将apply 你的单元格公式逻辑到每个A_Date 行值并输出为对应的Sold 列值。

import pandas as pd
from datetime import datetime

def is_sold(current_date:datetime, first_date:datetime, day_no:int)->str:
    # use of toordinal idea from @rjha94 https://stackoverflow.com/a/47478659
    if current_date.toordinal() - 693594 >= first_date.toordinal() - 693594 - day_no + 1:
        return "Yes" 
    else:
        return "No"

sales = pd.DataFrame({'Name':['RAHEAL','GIFTY','ERIC','PETER','EMMANUEL','BABA'],
                      'A_Date':['2020-04-30','2020-05-31','2020-03-16','2020-05-01','2019-12-15','2020-05-23']})
sales['A_Date'] = pd.to_datetime(sales['A_Date'], errors='coerce')
sales['Sold'] = sales['A_Date'].apply(lambda x:is_sold(x, sales['A_Date'][0],  x.day))
print(sales)

【讨论】:

  • df 应该是 sales 或反之亦然:)
  • 正确。我最初是为 df 写的,然后看到 OP 使用了 sales 并且始终没有更新。很好发现@YasserKhalil
【解决方案2】:
import pandas as pd

df = pd.DataFrame({'Name':['RAHEAL','GIFTY','ERIC','PETER','EMMANUEL','BABA'],
                   'A_Date':['04/30/2020','05/31/2020','03/16/2020',
                             '05/01/2020','12/15/2019','05/23/2020']})

df['A_Date'] = pd.to_datetime(df['A_Date'])
print(df)
df['Sold'] = df['A_Date'] >= df['A_Date'].iloc[0].replace(day=1)
df['Sold'] = df['Sold'].map({True:'Yes', False:'No'})
print(df)

输出:

       Name     A_Date
0    RAHEAL 2020-04-30
1     GIFTY 2020-05-31
2      ERIC 2020-03-16
3     PETER 2020-05-01
4  EMMANUEL 2019-12-15
5      BABA 2020-05-23

       Name     A_Date Sold
0    RAHEAL 2020-04-30  Yes
1     GIFTY 2020-05-31  Yes
2      ERIC 2020-03-16   No
3     PETER 2020-05-01  Yes
4  EMMANUEL 2019-12-15   No
5      BABA 2020-05-23  Yes

如果我正确阅读公式 - 如果 A_Date 值 >= 2020 年 4 月 1 日(即 B2 中日期的月份的第一天),那么 RAHEAL 也应该是“是”

我不知道您是否注意到(以及是否有意这样做),但如果 A_Date 值包含小数部分(即时间),那么当您计算该月 1 日的值时,就有可能出错。如果 B2 中的时间是上午 10:00,那么当您计算切割值时,它将是 2020 年 4 月 1 日 10:00。然后,如果您有另一个值,例如 04/01/2020 09:00,它将被评估为 False/No。这也是它在您的 Excel 公式中的工作原理。

编辑(2021 年 1 月 12 日):请注意,A_Date 列中的值属于 datetime.datetimedatetime.date 类型。大概是在读取 Excel 文件时或之后显式转换的。

【讨论】:

  • @QHarr,基于使用DAY 的公式,我假设它是Excel 中的日期,但无论如何我都会将它(我确实)转换为数据框中的datetime 对象。如果愿意,他们可以随时将其转换为 str
  • @YasserKhalil 如果没有看到您的代码,很难说出您的问题是什么。我展示的是我的代码和输出。如果您可以共享一些代码和完整的回溯。请注意,A_Date 列的值是 datetime.datetimedatetime.date 对象,而不是 str。我用datetime.datetime.replace()
  • @YasserKhalil,检查stackoverflow.com/q/28133018/4046632我编辑了我的答案以明确提及它。
  • @YasserKhalil,你是对的,iloc(0) 将返回列中的第一个值(即索引 0)(参见 this)。他们想要从 excel 参考 $B$2 转换的公式(单元格 B1 是标题)。 replace() - 我之前的评论中有链接。
  • @YasserKhalil,是的,它可以在一行中完成,但它的可读性和可读性较差。以慢动作而不是单行方式遵循代码会更容易。至少这就是我在不同的行中这样做的原因。
猜你喜欢
  • 2011-02-19
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-10
  • 1970-01-01
  • 1970-01-01
  • 2016-09-09
相关资源
最近更新 更多