Powershell (测量对象-求和)的备选方案。求和
我陷入了以下情况: 我必须从CSV文件中获取信息。我使用Powershell (测量对象-求和)的备选方案。求和,powershell,csv,sum,measure-object,Powershell,Csv,Sum,Measure Object,我陷入了以下情况: 我必须从CSV文件中获取信息。我使用导入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的列为关注值(“点数”) 这是我的第一个问题-->使用导入Csv,powershell会将此信息保存在[string]属性中。为了避
导入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
的列为关注值(“点数”)
这是我的第一个问题-->使用导入Csv
,powershell会将此信息保存在[string]
属性中。为了避免这种情况,我使用了以下行:
| Select @{Name="Points";Expression={[decimal]$_.Points}}
现在我得到了一个选中的.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]
,因为显示的数字是完全错误的。我想坚持使用十进制或整数,所以我有一个类似于71123.4
之类的输出
有没有其他方法,这样我就不必使用(measureobject-sum).sum
提前谢谢 我首先将所有发件人地址分组在一起,然后分别求和:
Import-Csv .\data.csv |Group-Object Sender |ForEach-Object {
[pscustomobject]@{
Sender = $_.Name
SumOfPoints = ($_.Group |Measure-Object Points -Sum).Sum
}
}
测量对象
将自动将点
字符串转换为[double]
-如果需要更高的精度,可以手动转换为[decimal]
,如前所述:
Import-Csv .\data.csv |Select-Object Sender,@{Name="Points";Expression={[decimal]$_.Points}} |Group-Object Sender |ForEach-Object {
[pscustomobject]@{
Sender = $_.Name
SumOfPoints = ($_.Group |Measure-Object Points -Sum).Sum
}
}
如前所述,通过使用分组,您可以在不损失小数精度的情况下获得总和:
# faking the Import-Csv here with a here-string.
# in real life, you would use: Import-Csv <yourdata.csv> -Delimiter ';'
$data = @"
Sender;Date;Description;Something;Number;Whatever;DontKnow;Email;Nothing;Zilch;Points;Empty;Nada
45227;01.10.2018 03:24:00;Xxxx Xxxx Xxxxx x XX xxxxxxxxxxxxxx Xxxxx xxx Xxxxxxxxxxxxxxxxxxx;;3;XXXV;XXXA;XXX@XX.com;;;3.7;;
45227;01.10.2018 03:24:00;Xxxx Xxxx Xxxxx x XX xxxxxxxxxxxxxx Xxxxx xxx Xxxxxxxxxxxxxxxxxxx;;3;XXXW;XXXB;XXX@XX.com;;;4.7;;
45226;01.10.2018 03:24:00;Xxxx Xxxx Xxxxx x XX xxxxxxxxxxxxxx Xxxxx xxx Xxxxxxxxxxxxxxxxxxx;;3;XXXX;XXXC;XXX@XX.com;;;4.777779;;
45225;01.10.2018 03:24:00;Xxxx Xxxx Xxxxx x XX xxxxxxxxxxxxxx Xxxxx xxx Xxxxxxxxxxxxxxxxxxx;;3;XXXY;XXXD;XXX@XX.com;;;4.8;;
45225;01.10.2018 03:24:00;Xxxx Xxxx Xxxxx x XX xxxxxxxxxxxxxx Xxxxx xxx Xxxxxxxxxxxxxxxxxxx;;3;XXXZ;XXXE;XXX@XX.com;;;4.9;;
"@ | ConvertFrom-Csv -Delimiter ';'
#get the two columns you need from the Csv and group them by Sender
$data | Select-Object Sender, Points | Group-Object Sender | ForEach-Object {
# add the 'Points' values as decimal
[decimal]$sum = 0
foreach ($value in $_.Group.Points) { $sum += [decimal]$value }
[PSCustomObject]@{
Sender = $_.Name
Sum = $sum
}
}
tl;博士: 如果需要控制用于对数字求和的特定数字数据类型:
- 避免使用总是使用
计算的[double]
测量对象
- 相反,使用LINQ(可在PSv3+中访问)并将转换为所需的数字类型:
有用的答案向您展示了一种优雅的方法,可以将共享相同电子邮件地址的行分组的
点
列相加,并通过将点真正相加为[decimal]
值进行改进
关于-Sum
和浮点数据类型的一些一般要点:
您正确地陈述了:
属性[数据类型]更改为double
[…]我发现Powershell的GenericMeasureInfo.Sum
属性只能返回一个null
作为属性值
确实:测量对象-总和
:
- 总是使用
值来汇总输入[double]
- 如果可能的话,它强制输入到
s,即使它们不是数字。[double]
- 如果无法将输入强制为
(例如,[double]
),则会发出非终止错误,但对任何剩余输入继续求和'foo'
- 如果无法将输入强制为
度量对象-Sum
输入,因为它们在求和过程中会根据需要转换为[double]
。
这意味着您可以直接使用导入Csv
命令,如以下示例所示(使用两个[pscustomobject]
实例模拟导入Csv
的输出):
71301.6000000006
[…]似乎我产生了“double”溢出
溢出将意味着超过a[double]
中可存储的最大值,这是(a)不太可能的([double]::MaxValue
为1.79769313486232E+308
,即大于308的10次方)和(b)将产生不同的症状;e、 g:
PS> ([double]::MaxValue, [double]::MaxValue | Measure-Object -Sum).Sum
∞ # represents positive infinity
然而,由于[double]
类型的内部二进制表示法,您得到的是舍入错误,它并不总是有精确的十进制表示法,这可能导致令人困惑的计算结果;e、 g:
PS> 1.3 - 1.1 -eq 0.2
False # !! With [double]s, 1.3 - 1.1 is NOT exactly equal to 0.2
有关详细信息,请参阅
使用[decimal]
值确实可以解决此问题,但请注意,这是以较小的范围为代价的(实际上,精度为28位小数-最大数字的绝对值取决于小数点的位置;作为整数,它是79228162514264337593543950335
,即接近8*1028)
如果您确实需要[decimal]
s的精度,则必须避免测量对象
,并自行求和
在原始命令的上下文中,可以使用Sum
LINQ方法:
[Linq.Enumerable]::Sum(
[decimal[]] @(
$Imported_CSV | where {$_.Sender -eq $Imported_CSV_Unique.Sender}
).Points
)
- 在管道命令周围使用
(数组子表达式操作符),而不仅仅是@(…)
,可以确保在管道恰好不返回任何行的情况下,整个命令不会失败。(…)
将非输出转换为空数组,对于该数组,@(…)
正确返回.Sum()
0
- 如果没有它,
强制转换将导致[decimal[]]
,PowerShell将无法找到$null
方法的.Sum()
类型的重载,并报告一个错误,“Sum”和参数计数发现多个不明确的重载:1”[decimal[]
- 如果没有它,
- 上面的命令总是要求将所有匹配的CSV行(表示为自定义对象)作为一个整体放入内存,而
(与PowerShell管道中的大多数cmdlet一样)将逐个处理它们,这只需要c度量对象
PS> ([double]::MaxValue, [double]::MaxValue | Measure-Object -Sum).Sum ∞ # represents positive infinity
PS> 1.3 - 1.1 -eq 0.2 False # !! With [double]s, 1.3 - 1.1 is NOT exactly equal to 0.2
[Linq.Enumerable]::Sum( [decimal[]] @( $Imported_CSV | where {$_.Sender -eq $Imported_CSV_Unique.Sender} ).Points )
# 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 }