2

我一直在使用 Scala 中的天气数据进行一些自主学习,因为这些数据是免费的、庞大的,并且可以用于我想做的许多其他事情。我立即遇到了如何表示超过 22 列的文本文件中的数据的问题。

处理超过 22 列的数据的惯用方式是什么?我一直在尝试阅读 NOAA 的NWS-USAF-NAVY 电台列表,他们每条线路有 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 或其他库来处理连接更好吗?

4

2 回答 2

2

您描述的问题比 Scala 更普遍。有两种表示某些数据的方法:以非规范化规范化形式非规范化数据是扁平的,更适合存储和传输,但更难为人类推理和管理。归一化的数据正好相反。

您以非规范化的形式获取数据。由于您计划在充满抽象和分类的高级语言中使用它,因此在解析时对这些数据进行规范化是很自然的。

一般实践表明,案例类的 22 个字段足以以规范化的形式表示任何类型的数据。您的情况也不例外,您已经在“LatLonElevation”示例中发现了这一点。

规范化您的数据

查看您的数据结构,很容易立即提取一些跨越多个字段的子实体:LatitudeLongitudeElevation。你可以看到实际上这三个本身是可分组的,因为它们都代表一个位置信息。然后您可以再次查看您的结构并看到有FAA LOC IDNWS Location IdentifierCountryState/Province AbbreviationCounty字段,移动到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 DB 框架可以很好地处理标准化数据。

于 2013-05-22T09:11:01.207 回答
2

如果你有这么多字段,你应该以某种方式对它们进行分组。

如果没有分组,你就不能让你的类不可变,你就不能利用case class生成的equalscopy.

很难使用具有这么多字段的类。

在您的情况下,位置180-219是一个字段“位置”,它应该分组在一个字段中。没有其他领域Latitude Seconds是完全没用的。Latitude *

只有 18 个字段,location您可以减少此数字。

对数据字段进行分组的自然方法是嵌套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使用一些解析器创建,以便以不同的格式重用它们。

例如:

class WbanParsers extends RegexParsers with LatLongParser {
  def wban: ... ~ latLonf ~ ...
  ...
}

trait LatLongParser {
  this: RegexParsers =>

  def latLong: ...
  def dms: ...
  ...
}
于 2013-05-22T04:19:29.840 回答