我一直在使用Scala中的天气数据进行一些自主学习,因为数据是免费的,很大的,并且可以提供给我想要做的很多其他事情。我立即遇到了如何用超过22列的文本文件表示数据的问题。
处理超过22列数据的惯用方法是什么?我一直试图在NOAA的NWS-USAF-NAVY station list阅读,每行有32条信息。
我最初的倾向是使用案例类,但最直接的方法是定义它们:
/*
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个项目的案例类,因为它使用元组来表示数据。
嵌套元组似乎是一种可能的解决方案,因此不是为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或其他库导入这种数据,还是会有与案例类相同的限制?顺便说一句,使用lazy val,Future或其他库来处理连接是否更好?
答案 0 :(得分:2)
如果你有这么多字段,你应该以某种方式对它们进行分组。
如果不进行分组,则无法使您的类不可变,您无法使用case class
的优势,例如生成的equals
和copy
。
很难使用包含这么多字段的类。
在您的情况下,位置180-219是单个字段“位置”,应将其分组到单个字段中。没有其他Latitude Seconds
字段,Latitude *
完全没用。
location
只有18个字段,您可以减少此数字。
分组数据字段的自然方式是嵌套的case
类。
要从字符串生成嵌套类,可以使用parsing combinator。这似乎有点矫枉过正,但你会得到一个干净的代码结构:
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
,以便以不同的格式重复使用它们。
例如:
class WbanParsers extends RegexParsers with LatLongParser {
def wban: ... ~ latLonf ~ ...
...
}
trait LatLongParser {
this: RegexParsers =>
def latLong: ...
def dms: ...
...
}
答案 1 :(得分:2)
您所描述的问题比Scala更为普遍。表示某些数据有两种方法:denormalized或normalized form。 非规范化数据是平坦的,更适合存储和传输,同时更难以推理和管理人类。 规范化数据正好相反。
您以非规范化形式获取数据。由于您计划在充满抽象和分类的高级语言中使用它,因此在解析时将这些数据规范化是很自然的。
通用实践表明,22个案例类的字段足以表示规范化形式的任何类型的数据。您的情况也不例外,您已经在自己的“LatLonElevation"示例
查看your data structure,您可以轻松地提取跨越多个字段的一些子实体:纬度,经度,海拔。你可以看到,事实上这三个人本身就是可分组的,因为它们都代表 Location 信息。您可以再次查看您的结构并查看 FAA LOC ID , NWS位置标识符,国家/地区,州/省缩写和县字段,进入 Location 也是有意义的。这样做最终会得到一个标准化的数据结构,该结构由一堆相互关联的案例类组成,每个类只有几个字段。
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
幸运的是,有一个SORM Scala数据库框架可以很好地处理规范化数据。