powershell - (Measure-Object -sum).Sum的替代

标签 powershell csv sum measure-object

我陷入以下情况:
我必须从CSV文件中获取信息。我使用Import-Csv导入了CSV。

我的原始数据如下所示:

45227;01.10.2018 03:24:00;Xxxx Xxxx Xxxxx x XX xxxxxxxxxxxxxx Xxxxx xxx Xxxxxxxxxxxxxxxxxxx;;3;XXXX;XXXX;XXX@XX.com;;;3.7;;

其中包含3.7的列是关注值(“点”)。

这是我的第一个问题->使用Import-Csv,powershell会将这些信息保存在[string]属性中。为了避免这种情况,我使用了以下行:
| Select @{Name="Points";Expression={[decimal]$_.Points}}

现在,我得到一个Selected.System.Management.Automation.PSCustomObject类型的对象,其中包含该属性作为[decimal]。现在,我想总结由同一电子邮件地址使用的所有要点:
$Data[$Index].Points += (
  $Imported_CSV | where {$_.Sender -eq $Imported_CSV_Unique.Sender} | 
    measure Points -sum
).Sum

这似乎工作得很好,但是如果我打开$Data[$Index] | gm,我会得到:Points NoteProperty double Points=71301.6000000006
该属性已更改为[double]。我挖了一点,发现Powershell的GenericMeasureInfo.Sum属性只能返回Nullable<Double>实例作为属性值。

似乎我正在产生[double]溢出,因为显示的数字是完全错误的。我想坚持十进制或整数,所以我有像71123.4或类似的输出。

还有其他方法,所以我不必使用(Measure-Object -sum).Sum

提前致谢!

最佳答案

tl;博士:

如果需要控制特定的数字数据类型用于对数字求和:

  • 避免使用Measure-Object,后者始终使用[double]计算。
  • 而是使用 LINQ Sum method (可在PSv3 +中访问),并将强制转换为所需的数字类型:

  • [Linq.Enumerable]::Sum(
      [decimal[]] @(
        $Imported_CSV | where {$_.Sender -eq $Imported_CSV_Unique.Sender}
      ).Points
    )
    

    Mathias R. Jessen's有用的答案向您展示了一种优雅的方式,可以将Points列按共享相同电子邮件地址的行进行分组,而Theo's helpful answer通过将这些点真正地求和为[decimal]值来进行改进。

    有关 Measure-Object -Sum以及浮点数据类型的一些一般要点:

    您正确声明:

    The property [data type] changed to double [...] i found out that Powershell's GenericMeasureInfo.Sum property can only give back a Nullable<Double> as property value.



    确实:Measure-Object -Sum:
  • 始终使用[double]值来汇总输入。
  • 将输入强制为[double],即使可能不是数字,也可以。
  • 如果无法将输入强制转换为[double](例如'foo'),则会发出非终止错误,但将对所有剩余输入继续求和。

  • 上面的内容意味着甚至是字符串都是Measure-Object -Sum 的可接受输入,因为它们在求和期间会按需转换为[double]
    这意味着您可以直接使用Import-Csv命令,如以下示例所示(该示例使用两个[pscustomobject]实例来模拟Import-Csv的输出):
    PS> ([pscustomobject] @{ Points = '3.7' }, [pscustomobject] @{ Points = '1.2' } |
          Measure-Object Points -Sum).Sum
    4.9  # .Points property values were summed correctly.
    

    71301.6000000006 [...] It seems like i'm producing an overflow of "double"



    溢出将意味着超过可以存储在[double]中的最大值,这是(a)不太可能的([double]::MaxValue1.79769313486232E+308,即大于308的幂的10),并且(b)会产生不同的症状;例如。:
    PS> ([double]::MaxValue, [double]::MaxValue | Measure-Object -Sum).Sum
    ∞  # represents positive infinity
    

    但是,您得到的是四舍五入 错误,这是因为[double]类型的内部二进制表示形式,而该二进制表示形式并不总是具有精确的十进制表示形式,这会导致计算结果令人困惑。例如。:
    PS> 1.3 - 1.1 -eq 0.2
    False # !! With [double]s, 1.3 - 1.1 is NOT exactly equal to 0.2
    

    有关更多信息,请参见https://floating-point-gui.de/

    使用[decimal]值确实解决了这个问题,但是请注意,这是以较小范围为代价的(实际上,您获得28个十进制数字的精度-最大数字的绝对值取决于小数点的位置) ;作为整数,它是79,228,162,514,264,337,593,543,950,335,即接近8 * 1028)。

    如果确实需要[decimal]的精度,则必须避免Measure-Object并自己对求和。

    在原始命令的上下文中,可以使用Sum LINQ方法:
    [Linq.Enumerable]::Sum(
      [decimal[]] @(
        $Imported_CSV | where {$_.Sender -eq $Imported_CSV_Unique.Sender}
      ).Points
    )
    
  • 在管道命令周围使用@(...)(数组子表达式运算符),而不只是(...),可确保在管道不返回任何行的情况下,总体命令不会失败。 @(...)将非输出转换为一个空数组,为此.Sum()正确返回0
  • 如果没有它,则[decimal[]]强制转换将导致$null,而PowerShell将无法找到[decimal[]]方法的.Sum()类型的重载并报告错误,“为“Sum”找到多个模棱两可的重载,并且参数计数为:1 ”。
  • 上面的命令始终要求将所有匹配的CSV行(表示为自定义对象)整体上作为内存,而Measure-Object(作为PowerShell管道中的大多数cmdlet)将一个接一个地处理它们,这仅需要恒定数量的内存(但较慢)。

  • 如果无法将所有匹配的行一次加载到内存中,请使用ForEach-Object(foreach)cmdlet,但是请注意,只有将实际的Import-Csv调用替换为内存数组$Imported_Csv时,这才有意义:
    # Replace $Imported_Csv with the original Import-Csv call to 
    # get memory-friendly one-by-one processing.
    $Imported_CSV | where {$_.Sender -eq $Imported_CSV_Unique.Sender} |
      foreach -Begin { [decimal] $sum = 0 } -Process { $sum += $_.Points } -End { $sum }
    

    关于powershell - (Measure-Object -sum).Sum的替代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55205714/

    相关文章:

    xml - 如何循环遍历分组的 XML 节点

    powershell - 在PowerShell中获取以P开头的目录

    postgresql - Postgres连续累计计数

    JQuery:计算输入字段总数的表单...但同一页面上有此表单的多个实例

    oracle - 相同的代码但不同的结果

    readr::read_csv() 不读取日期并返回 NA

    Python 帮助读取由于行尾而失败的 csv 文件

    python - 使用 Python 从文件中查找多个最大值

    python - 对字典值进行 SUMIF 的好方法是什么?

    powershell - 使用 C# 开发 PowerShell Cmdlet : where to place the "main code"