在Scala中解析具有多个属性的数据

在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

我一直在用Scala中的天气数据进行一些自我指导的学习,因为这些数据是免费的、大的,并且可以输入到我想做的很多其他事情中。我很快就遇到了如何从一个超过22列的文本文件中表示数据的问题

处理超过22列数据的惯用方法是什么?我一直在努力阅读国家海洋和大气管理局的报告,他们每行有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项的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”示例中发现了自己

规范化数据 看一下,很容易立即提取跨越多个字段的一些子实体