在Scala中解析具有多个属性的数据
我一直在用Scala中的天气数据进行一些自我指导的学习,因为这些数据是免费的、大的,并且可以输入到我想做的很多其他事情中。我很快就遇到了如何从一个超过22列的文本文件中表示数据的问题 处理超过22列数据的惯用方法是什么?我一直在努力阅读国家海洋和大气管理局的报告,他们每行有32条信息 我最初倾向于使用用例类,但最直接的定义方式是:在Scala中解析具有多个属性的数据,scala,record,etl,Scala,Record,Etl,我一直在用Scala中的天气数据进行一些自我指导的学习,因为这些数据是免费的、大的,并且可以输入到我想做的很多其他事情中。我很快就遇到了如何从一个超过22列的文本文件中表示数据的问题 处理超过22列数据的惯用方法是什么?我一直在努力阅读国家海洋和大气管理局的报告,他们每行有32条信息 我最初倾向于使用用例类,但最直接的定义方式是: /* first goal is to be able to read the inventory of WBAN stations from NOAA at
/*
first goal is to be able to read the inventory of WBAN stations from NOAA at
ftp.ncdc.noaa.gov/pub/data/inventories/WBAN.TXT
formats are listed in:
ftp://ftp.ncdc.noaa.gov/pub/data/inventories/WBAN-FMT.TXT
although I don't think that file is completely right
*/
case class WBAN(
CoopStationID: Option[String], // 01 - 06 Coop Station Id
ClimateDivision: Option[String], // 08 - 09 Climate Division
WBANStationID: Option[String], // 11 - 15 WBAN Station Id
WMOStationID: Option[String], // 17 - 21 WMO Station Id
FAALOCID: Option[String], // 23 - 26 FAA LOC ID
// and so on, for 32 elements!
是不允许的,因为scala不允许超过22项的case类,因为它使用元组来表示数据
嵌套元组似乎是一种可能的解决方案,因此可以嵌套纬度、经度、海拔等内容,而不是为NOAA列出的每个项目设置字段:
// class representing a latitude or longitude's information
case class DMS(
Degrees: Int,
Minutes: Int,
Seconds: Int
)
// class combining a lat lon with elevation data
case class LatLonElevation(
Latitude: DMS,
Longitude: DMS,
LatLonPrecision: String,
ElevationGround: Option[Int],
Elevation: Option[Int],
ElevationTypeCode: Option[Int]
)
或者你把它放到一张地图上,每个值都有一个向量
似乎应该有一种简洁的方法来实现这一点,但在实现它之后,我最终以不同的格式重复了相当多的含义,这非常难看。是否有某种方法可以使用SLICK或其他库导入此类数据,或者它是否具有与case类相同的限制?另外,使用lazy val、Future或其他库来处理连接是否更好?如果有这么多字段,您应该以某种方式对它们进行分组 如果不进行分组,就无法使类保持不变,也无法利用
案例类
的优点,例如生成的等于
和复制
使用具有如此多字段的类是很困难的
在您的情况下,位置是一个单独的字段“位置”,它应该分组在一个单独的字段中<如果没有其他Latitude*
字段,代码>纬度秒将完全无效
只有18个字段具有位置
,您可以减少此数字
对数据字段进行分组的自然方式是嵌套case
类
要从字符串生成嵌套类,可以使用。这似乎有点过分,但您将得到一个干净的代码结构:
class WbanParsers extends RegexParsers {
def wban: Parser[Wban] = stationId ~ .... ~ latLong ~ ... ^^
{ case sid ~ ... ~ latLong ~ ... => Wban(sid, ..., latLong, ...) }
...
def latLong: Parser[LatLonElevation] = dms ~ dms ~ ... ^^
{ case lat ~ long ~ ... => LatLonElevation(lat, long, ...) }
def dms: Parser[Dms] = (" " ^^^ {Positive} | "-" ^^^ {Negative}) ~ degrees ~ minutes ~ seconds ^^
{ case sign ~ d ~ m ~ s => Dms(sign, d, m, s) }
...
}
您可以使用一些解析器创建trait
,以便以不同的格式重用它们
例如:
WBanParser类使用LatLongParser扩展regexparser{
def wban:…~latLonf~。。。
...
}
特征LatLongParser{
这个:RegexParsers=>
def latLong:。。。
def dms:。。。
...
}
如果您有这么多字段,您应该以某种方式对它们进行分组
如果不进行分组,就无法使类保持不变,也无法利用案例类
的优点,例如生成的等于
和复制
使用具有如此多字段的类是很困难的
在您的情况下,位置是一个单独的字段“位置”,它应该分组在一个单独的字段中<如果没有其他Latitude*
字段,代码>纬度秒将完全无效
只有18个字段具有位置
,您可以减少此数字
对数据字段进行分组的自然方式是嵌套case
类
要从字符串生成嵌套类,可以使用。这似乎有点过分,但您将得到一个干净的代码结构:
class WbanParsers extends RegexParsers {
def wban: Parser[Wban] = stationId ~ .... ~ latLong ~ ... ^^
{ case sid ~ ... ~ latLong ~ ... => Wban(sid, ..., latLong, ...) }
...
def latLong: Parser[LatLonElevation] = dms ~ dms ~ ... ^^
{ case lat ~ long ~ ... => LatLonElevation(lat, long, ...) }
def dms: Parser[Dms] = (" " ^^^ {Positive} | "-" ^^^ {Negative}) ~ degrees ~ minutes ~ seconds ^^
{ case sign ~ d ~ m ~ s => Dms(sign, d, m, s) }
...
}
您可以使用一些解析器创建trait
,以便以不同的格式重用它们
例如:
WBanParser类使用LatLongParser扩展regexparser{
def wban:…~latLonf~。。。
...
}
特征LatLongParser{
这个:RegexParsers=>
def latLong:。。。
def dms:。。。
...
}
您描述的问题比Scala更一般。有两种方法来表示某些数据:在a或a中。非规范化数据是扁平的,更适合存储和传输,同时更难为人类进行推理和管理。标准化数据正好相反
您以非规范化的形式获取数据。因为您计划在一种充满抽象和分类的高级语言中使用它,所以在解析时规范化这些数据是很自然的
一般实践表明,22个case类字段足以以规范化形式表示任何类型的数据。您的案例并不例外,您已经在“LatLonElevation”示例中发现了这一点
规范化数据
通过查看,很容易立即提取跨越多个字段的子实体:纬度、经度、高程。您可以看到,事实上这三个本身是可分组的,因为它们都表示位置信息。然后,您可以再次查看您的结构,并看到有FAA LOC ID、NWS位置标识符、国家、州/省缩写和县字段,移动到该位置也是有意义的。这样,您将得到一个规范化的数据结构,该结构由一组interreferring case类组成,每个类只包含几个字段
case class Station
( coopId : String,
wbanId : String,
wmoId : String,
icaoId : String,
location : Location )
case class Location
( faaId : String,
nwsId : String,
country : String,
stateOrProvince : Option[ String ],
county : Option[ String ],
latitude : Latitude,
longitude : Longitude,
elevation : Elevation )
case class Latitude
( direction : LatitudeDirection.Value,
degrees : Int,
minutes : Int,
seconds : Int )
object LatitudeDirection extends Enumeration {
val North, South = Value
}
// and so on
存储这些数据
幸运的是,有一个Scala DB框架可以很好地处理规范化数据。您描述的问题比Scala更一般。有两种方法来表示某些数据:在a或a中。非规范化数据是扁平的,更适合存储和传输,同时更难为人类进行推理和管理。标准化数据正好相反 您以非规范化的形式获取数据。因为您计划在一种充满抽象和分类的高级语言中使用它,所以在解析时规范化这些数据是很自然的 一般实践表明,22个case类字段足以以规范化形式表示任何类型的数据。你的案例并不例外,你已经在你的“LatLonElevation”示例中发现了自己 规范化数据 看一下,很容易立即提取跨越多个字段的一些子实体