groovy语言 累加
在关于Groovy编程语言入门的第一篇文章中 ,我以在Groovy中读取CSV文件的示例为例。 在本文中,我将转向一种更惯用的Groovy风格(就像某些人所说的那样使其成为groovier),涵盖将Groovy映射用作查找表,最后通过使用映射来计算一些结果。
首先,第一件事—这是上一篇文章的最后一个示例,使用了更加惯用的Groovy:
import com.opencsv.CSVReader
def mdCountryCSV = "Metadata_Country_API_SP.POP.TOTL_DS2_en_csv_v2.csv"
new File(mdCountryCSV).withReader { reader ->
def csvReader = new CSVReader(reader)
def fNam = csvReader.readNext()
csvReader.each { fVal ->
def valByNam = [fNam,fVal].transpose().collectEntries()
println valByNam
}
}
将此代码段保存为ex04.groovy
,与前三个示例保存在同一目录中,并将您从世界银行下载的数据保存并运行。 你看到了什么?
一些解释是按顺序进行的。
首先,为了使示例更具可读性,我缩短了长变量名。
其次,Groovy对类型检查的看法与Java不同。 正如Burt Beckwith在其关于Grails(和Groovy)的出色著作中所描述的“ Programming Grails ”一样,Groovy支持可选的键入,允许用户通过使用def
关键字来控制键入或由运行时决定的语言来决定类型。声明变量。 我将在伯特的精彩论述中加上一句古老的格言:“强大的力量伴随着巨大的责任”。 将类型决策留给语言决定会引入难以发现的错误,并且在任何情况下都会推迟到运行时才检测到任何类型错误。 但是,这种语言设计决策也意味着Groovy不仅可以提供更简洁的代码(更少的阅读量意味着更少的调试量),而且还可以提供一些有趣的动态功能,这些功能与在运行时实现行为有关。
第三,用于复制文件名每一行的csvReader
提供的列名数组和列值的对应数组的简洁代码已被Groovy的一些不错的方法处理集合和映射:
-
[fNam, fVal]
创建一个新列表,该列表由两个元素组成:字段名称数组和当前行上的字段值数组 -
.transpose()
将其转换为新列表,其中每个元素是每个字段的一对[name,value] -
.collectEntries()
将[name,value]对的列表转换为映射,其中键是字段名称,而值是字段值
最后,行println valByNam
仅转储由上面生成的地图。
好的,足够的解释。 上面的代码只是非常有趣,因为,实际上,谁需要将国家元数据的内容重新设置为地图/键值对? 我们来处理这些数据!
创建查找表
我们在国家元数据中看到的一件事是,世界银行拥有一个根据国家收入进行分类的系统。 该列在文件中称为IncomeGroup (无空格)。 作为将两个单独的文件链接在一起的示例,让我们:
- 根据国家/地区元数据创建国家/地区代码与收入组的对照表-我们将其称为
iGLU;
- 使用该查找表从人口数据文件中按收入组计算人口增长率。
为此,我们首先需要声明一个查找表并填充它。 我们将重新利用上面的代码:
import com.opencsv.CSVReader
def iGLU = [:] // income group lookup table
def mdCountryCSV = "Metadata_Country_API_SP.POP.TOTL_DS2_en_csv_v2.csv"
new File(mdCountryCSV).withReader { reader ->
def csvReader = new CSVReader(reader)
def fNam = csvReader.readNext()
csvReader.each { fVal ->
def valByNam = [fNam,fVal].transpose().collectEntries()
iGLU[valByNam."Country Code"] = valByNam.IncomeGroup
}
}
println iGLU
将此代码另存为ex05.groovy并运行。
表示法[:]
创建一个空的地图。 例如,我们可以通过定义一个基于哈希表的映射来更具体,该映射采用String参数并产生一个字符串结果。 如果您不太熟悉地图和哈希表, 则Groovy文档的2.2节提供了更多详细信息。
另请注意,使用点符号来访问地图-特别是, IncomeGroup
组周围的引号不是必需的,而Country Code
中的引号则没有必要,因为其内嵌空格。 但是,如果点右边是变量而不是常量,我们要么必须将其括在圆括号中,要么返回方括号并省略点。
创建和初始化累加器
为了按收入组计算人口增长,我们将不得不创建累加器。 由于我们使用的是国家/地区元数据中提供的收入组分类,因此将累加器定义为地图是有意义的。 另外,我们将需要两个:一个用于开始年份的人口,一个用于结束年份的人口。 最后,我们需要确定是在开始时还是在读取人口数据时遇到新的收入群体时初始化这些指标。 出于本练习的目的,我们将首先初始化:
def iGSet = iGLU.values() as Set
def pop1 = [:] // population by income group in start year
def pop2 = [:] // population by income group in end year
iGSet.each { ig ->
pop1[ig] = 0l
pop2[ig] = 0l
}
将此附加到ex05.groovy
的末尾(可以删除println
)。
代码的第一行从iGLU
收入组查找图中获取所有值,并将它们转换为Set
,这是一种集合,其中每个元素最多出现一次。 我们将使用此iGSet
迭代国家/地区元数据中定义的收入组的唯一值。
然后,我们定义两个累加器, pop1
(用于在开始年度中按收入组累加总计), pop2
(用于结束年度)。
接下来的四pop1
和pop2
初始化为零(长—就是0l
意思)。 现在是要注意的一个好时机,Groovy的设计师认为Groovy源代码中的不合格整数是BigInteger
类型,而不合格的十进制数是BigDecimal
类型。 我倾向于避免使用这些(软件实现的)类型。
使用查找处理另一个文件并累积
此时,我们可以读取人口数据并进行累积:
def populationCSV = "API_SP.POP.TOTL_DS2_en_csv_v2.csv"
new File(populationCSV).withReader { reader ->
def csvReader = new CSVReader(reader)
def fNam
(1..5).each { fNam = csvReader.readNext() }
csvReader.each { fVal ->
def valByNam = [fNam,fVal].transpose().collectEntries()
def country = valByNam."Country Code"
if (country && iGLU.containsKey(country)) {
pop1[iGLU[country]] += Long.parseLong(valByNam."2014" ?: "0")
pop2[iGLU[country]] += Long.parseLong(valByNam."2015" ?: "0")
}
}
}
将此附加到ex05.groovy的末尾 。
与元数据文件的处理(毕竟都是CSV)类似,值得注意的是总体CSV格式不正确-在列标题行之前有四个标题行。 因此,定义列名列表更加复杂。 代码(1..5)
使用Groovy的范围 ,以产生5个元素1,2,3,4,5的列表,并且each
执行闭合一次为每个元件。 for (int i = 0; i < 5; i++) {...}
这实际上等效于C或Java(或Groovy!) for (int i = 0; i < 5; i++) {...}
但是不需要我们定义虚假变量。 通过此代码, fNam
最终设置为第五行,即列标题。
if语句首先检查该国家/地区是否为非null和非空白,然后确保在收入组查找表的键中找到该国家/地区。 这是个休息时间,学习一些Groovy语义的好时机,尤其是Groovy中的真理的含义(该页的第5节)。 基本上,非null,非空,非零值是true。
总结结果
最后,我们在这一点上使用Long
值,因为有太多的人无法枚举32位整数。
现在我们已经积累了数据,剩下的第一步是将其写出:
iGSet.each { ig ->
printf "group %s growth rate %.2f %\n",
(ig ?: "Unspecified"),100d * (pop2[ig] - pop1[ig]) / pop1[ig]
}
将此附加到ex05.groovy的末尾 。 至此,您已经拥有完整的程序并可以运行它,并产生以下输出:
group High income growth rate 0.56 %
group Low income growth rate 2.73 %
group Upper middle income growth rate 0.78 %
group Unspecified growth rate 1.27 %
group Lower middle income growth rate 1.46 %
请注意,上面的(ig ?: "Unspecified")
使用Groovy Elvis运算符 ,它是(ig ? ig : "Unspecified")
的缩写形式,并且是DRY原理的一个很好的示例(“不要自己重复”)。 我们正在使用此构造,以便每个收入组都打印一个文本字符串,即使未在原始数据中指定该字符串也是如此。
作为数据分析师,我的目的不是解释这些结果。 但是,我收集一些数据(在这种情况下,是公开可用的)并将其挖掘用于建立关系的整个过程正是我很多工作要做的。 通过这个示例,您可以了解为什么Groovy简洁而强大的集合和映射抽象,再加上那里的所有Java库,使其成为我必不可少的工具。
下一步要去哪里?
在下一部分中,我们将介绍CSV文件之外的其他结构化数据源。
翻译自: https://opensource.com/article/16/12/dealing-data-getting-groovy-part-ii
groovy语言 累加