分享

Spark 高级分析:第八章第3-5节 利用Scala处理时间空间数据

feilong 2018-9-21 17:01:53 发表于 连载型 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 1 8324
问题导读

1.
JodaTime和NScalaTime分别是什么?如何使用?
2.
Esri Geometry API作用是什么?如何获取?如何使用?
3.样例数据中的时间和空间数据分别用何种方法处理?



关注最新经典文章,欢迎关注公众号



上一篇:Spark 高级分析:第八章第1,2节 获取数据并使用
http://www.aboutyun.com/forum.php?mod=viewthread&tid=25232


第3节 JodaTime and NScalaTime处理时间数据

对于时间数据,当然有Java Date类和Calendar类。但是任何使用这些库的人都知道,它们很难使用,并且需要大量的样板来进行简单的操作。多年来,JodaTime一直是处理时间数据的Java库。

有一个为Scala提供JodaTime提供了额外的语法糖,名为NScalaTime的包装库。我们可以通过一个引入来使用它的所有函数:
[mw_shl_code=scala,true]import com.github.nscala_time.time.Imports._[/mw_shl_code]
JodaTime和NScalaTime都是基于DateTime类。DateTime对象是不可变的,如Java字符串(与常规Java API中的Calendar/Date对象不同),并提供了一些可以用于对时间数据执行计算的方法。在下面的例子中,dt1代表2014年9月4日上午9点,dt2代表2014年10月31日下午3点:
[mw_shl_code=scala,true]val dt1 = new DateTime(2014, 9, 4, 9, 0)
dt1: org.joda.time.DateTime = 2014-09-04T09:00:00.000-07:00
dt1.dayOfYear.get
res60: Int = 247
val dt2 = new DateTime(2014, 10, 31, 15, 0)
dt2: org.joda.time.DateTime = 2014-10-31T15:00:00.000-07:00
dt1 < dt2
res61: Boolean = true
val dt3 = dt1 + 60.days
dt3: org.joda.time.DateTime = 2014-11-03T09:00:00.000-08:00
dt3 > dt2
res62: Boolean = true[/mw_shl_code]
对于一些数据分析的问题,我们通常需要将日期字符串表示为可计算的DateTime对象。实现这一点的一个简单方法是Java的SimpleDateFormat ,对于解析不同格式的日期是有用的。下面对出租车数据集所使用的格式进行解析:
[mw_shl_code=scala,true]import java.text.SimpleDateFormat
val format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date = format.parse("2014-10-12 10:30:44")
val datetime = new DateTime(date)[/mw_shl_code]
当我们解析DateTime对象时,我们经常要对他们做一种时域算法找出多少秒或几小时或几天内把它们分开。在JodaTime,我们用Duration类代表一个时间跨度,这可以通过两个DateTime实例实现:
[mw_shl_code=scala,true]val d = new Duration(dt1, dt2)
d.getMillis
d.getStandardHours
d.getStandardDays[/mw_shl_code]
JodaTime在执行这些持续时间计算时处理不同时区的所有单调的细节和日历的怪异,比如夏令时,这样就不必担心它们了。

第4节 用Esri Geometry API 和 Spray处理地理数据

在JVM上的时间数据的工作很简单:只要使用JodaTime,也许一个像NscalaTime的封装会使你的分析更容易理解。对于地理空间数据,答案并不是那么简单;有许多不同的库和工具,它们具有不同的功能、发展状态和成熟度级别,因此对于所有地理空间用例来说,没有一个占主导地位的Java库。

第一个问题:你有什么样的地理空间数据?有两种主要的类型:向量和栅格,并且有不同的工具来处理不同类型的数据。在我们的示例中,出租车记录的纬度和经度以及以GeoJSON格式存储的矢量数据表示纽约不同行政区的边界。因此,我们需要一个能够解析GeoJSON数据并能够处理空间关系的库,比如检测给定的经度/纬度对是否包含在表示特定行政区边界的多边形内。

不幸的是,没有一个开源库正好符合我们的需求。GeoJSON解析器库可以将GeoJSON转换成Java对象,但是没有一个相关的地理空间库可以分析生成对象上的空间关系。这里是GeoTools项目,但是它有一个长长的组件和依赖项列表,这正是我们在从Spark shell选择要处理的库时试图避免的。最后,有用于Java的Esri Geometry API,它具有很少的依赖性并且可以分析空间关系,但是只能解析GeJJSON标准的子集,因此不做一些初步的数据处理,它将无法解析我们下载的GeoJSON数据。

对于数据分析师来说,缺乏工具可能是一个无法克服的问题。但我们是数据科学家:如果我们的工具不支持我们解决问题,那么我们就创建新的工具。在这种情况下,我们将通过利用支持解析JSON数据的许多Scala项目之一,添加用于解析所有GeoJSON数据的Scala函数,包括Esri Geometry API不处理的位。我们将在接下来的几节中讨论的代码可以在本书的git repo中获得,但是也可以作为GitHub上的独立库在http://github.com/jwills/geojson上获得,在http://github.com/jwills/geojson上,可以将其用于Scala中的任何类型的地理空间分析项目。

第5节 探索Esri Geometry APIESRI库的核心数据类型是几何对象。几何学描述的是一种形状,伴随着一种地理位置。该库包含一组空间操作,允许分析几何结构及其关系。这些操作可以告诉我们几何的区域,告诉我们两个几何体是否重叠,或者计算由两个几何体的结合所形成的几何结构。

在我们的例子中,我们将使用表示出租车出行点(经度和纬度)的几何对象,以及表示纽约市一个行政区边界的几何对象。我们感兴趣的空间关系是包容:空间中的给定点是否位于与曼哈顿区相关的一个多边形内?

Esri API提供了名为GeometryEngine的便利类,该类包含用于执行所有空间关系操作(包括contains操作)的静态方法。contains方法接受三个参数:两个Geometry对象和一个SpatialReference类的实例,该类表示用于执行地理空间计算的坐标系。为了获得最大的精度,我们需要分析相对于坐标平面的空间关系,该坐标平面将地球这个畸形球体上的每个点映射到二维坐标系中。地理空间工程师有一组标准的众所周知的标识符(称为WKID),可以用来引用最常用的坐标系。为了我们的目的,我们将使用WKID 4326,它是GPS所使用的标准坐标系统。

作为Scala开发人员,我们一直在寻找减少作为SparkShell中交互式数据分析的一部分需要执行的键入量的方法,因为在SparkShell中,我们无法访问能够自动完成长方法名的Eclipse和IntelliJ等开发环境。对我们来说,提供一些语法糖,让它更容易阅读某些类型的操作。遵循我们在NScalaTime库中看到的命名约定(它定义了像RichDateTime和RichDuration这样的包装器类),我们将定义我们自己的RichGeometry类,该类使用一些有用的帮助方法扩展Esri Geometry对象:
[mw_shl_code=scala,true]import com.esri.core.geometry.Geometry
import com.esri.core.geometry.GeometryEngine
import com.esri.core.geometry.SpatialReference
class RichGeometry(val geometry: Geometry,
val spatialReference: SpatialReference =
SpatialReference.create(4326)) {
def area2D() = geometry.calculateArea2D()
def contains(other: Geometry): Boolean = {
GeometryEngine.contains(geometry, other, spatialReference)
}
def distance(other: Geometry): Double =
GeometryEngine.distance(geometry, other, spatialReference
}
}[/mw_shl_code]
我们还将为RichGeometry声明一个伴生对象,该对象支持隐式地将Geometry类的实例转换为RichGeometry实例:
[mw_shl_code=scala,true]object RichGeometry {
implicit def wrapRichGeo(g: Geometry) = {
new RichGeometry(g)
}
}[/mw_shl_code]
记住,为了能够利用这种转换,我们需要将隐式函数定义导入到Scala环境中,如下所示:
[mw_shl_code=scala,true]import RichGeometry._[/mw_shl_code]


已有(2)人评论

跳转到指定楼层
jiangzi 发表于 2018-9-21 20:51:59
Spark 高级分析:第八章第3-5节~~~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条