原文地址:https://www.pilosa.com/docs/latest/examples/

位图数据库:Pilosa查询十亿级出租车搭乘数据案例

简单说明 Introduction

纽约市发布了一个非常详细的包含了超过10亿条出租车搭乘数据的集合。该数据已经成为科技博客分析的热门目标,并且已经得到了很好的研究。出于这个原因,我们认为将这些数据导入Pilosa,以便确定同一数据集情况下与其他数据存储和技术进行比较。

一般来说,传输(Transportation)是Pilosa的值得关注的用例,因为它通常涉及多个不同的数据源,以及高速率,实时和极大量的数据(特别是如果想得出合理的结论)。

我们编写了一个工具来帮助将NYC(纽约市)出租车数据导入Pilosa这个工具是PDK(Pilosa开发工具包)的一部分,并利用了许多可重复使用的模块,这些模块也可以帮助您导入其他数据。 接下来,我们将逐步解释整个过程。

初始设置之后,PDK导入工具会执行我们定义Pilosa架构所需的一切,相应地将数据映射到位图,然后将其导入Pilosa

数据模型 Data Model

纽约出租车数据由以下列出的许多csv文件组成:http://www.nyc.gov/html/tlc/html/about/trip_record_data.shtml。 这些数据文件大约有20列,其中大约一半与我们正在研究的基准查询相关:

  • 距离(Distance): miles(英里), floating point(浮点值)
  • 车费(Fare): dollars(美元), floating point(浮点值)
  • 乘客人数(Number of passengers): integer(整数值)
  • 下车位置(Dropoff location): latitude and longitude(经纬度), floating point(浮点值)
  • 上车位置(Pickup location): latitude and longitude(经纬度), floating point(浮点值)
  • 下车时间(Dropoff time): timestamp(时间戳)
  • 上车时间(Pickup time): timestamp(时间戳)

注意:下面表格中的row ID是指记录的取值范围,不要理解成MySQL等数据库的rowID。

我们导入这些字段,从每个字段创建一个或多个Pilosa字段:

字段(Field) 映射(Mapping)
cab_type(出租车类型) 直接映射整数枚举值 → row ID
dist_miles(距离) 四舍五入round(dist) → row ID
total_amount_dollars(总金额) 四舍五入round(dist) → row ID
passenger_count(乘车人数) 直接映射整数值 → row ID
drop_grid_id(下车位置网格ID) (lat, lon) → 100x100矩形分割网格 → cell(格子) ID
drop_year 年份year(timestamp) → row ID
drop_month 月份month(timestamp) → row ID
drop_day 该月第几天day(timestamp) → row ID
drop_time(下车时间) 该天中的时间映射到48个半小时组成的桶中
pickup_grid_id(下车位置网格ID) (lat, lon) → 100x100矩形分割网格 → cell ID
pickup_year year(timestamp) → row ID
pickup_month month(timestamp) → row ID
pickup_day day(timestamp) → row ID
pickup_time(上车时间) time of day mapped to one of 48 half-hour buckets → row ID

我们还创建了两个附加字段表示持续时间和每一次乘坐的平均速度:

字段(Field) 映射(Mapping)
duration_minutes(持续时间) round(drop_timestamp - pickup_timestamp) → row ID
speed_mph() round(dist_miles ÷ (drop_timestamp - pickup_timestamp)) → row ID

映射Mapping

我们要使用的每个列(column)都必须根据某些规则映射到字段(fields)row ID的组合。 有很多方法可以实现这个映射,出租车数据集为我们提供了一个可能性的很好的描述。

0列(colums) --> 1字段(field)

cab_type: 每一行表示一个出租车的类型,在一行数据中有一些bit位来表示一次乘车中使用的出租车类型。 这是一个简单的枚举映射,例如黄色yellow=0,绿色green=1等。这个字段值的位宽(bits)由数据源确定。也就是说,我们有几个数据来源(NYC出租车-黄色, NYC出租车-绿色,Uber汽车),对于每一个数据来源,要设置不同的cab_type常量值。

1列(colums) --> 1字段(field)

以下三个字段以简单的直接方式从原始数据的单个列进行映射。

dist_miles:每一行值(row ID)表示乘车的距离,这个映射关系很简单:例如行值1(row 1)表示乘车距离在[0.5,1.5]这个区间内。也就是说,我们将距离这个浮点值舍入为整数并将其直接用作row ID。通常,从浮点值到row ID的映射可以是任意的。

舍入映射实现简洁,简化了导入和分析。并且,它是人类可读的。 我们会看到这种模式多次使用。

PDK使用中,我们定义了一个Mapper,它是一个只返回整数row ID的函数。 PDK具有许多预定义的映射器,可以使用一些参数进行描述。 其中之一是LinearFloatMapper。在下面代码中它将线性函数应用于输入,并将其转换为整数,因此隐式处理舍入。

lfm := pdk.LinearFloatMapper{
Min: -0.5,
Max: 3600.5,
Res: 3601,
}

MinMax定义线性函数,Res确定输出row ID的最大允许值。我们选择这些值以产生一个舍入到最接近的整数的行为。其他预定义的映射器也有自己的特定参数,通常是两个或三个。

这个映射函数(mapping function)是核心操作,但我们需要一些其他部分来定义整个过程,它封装在BitMapper对象中。此对象定义要使用的输入数据源的哪些字段(Field),如何解析它们(Parsers),要使用的映射(Mapper)以及要使用的字段的名称(Frame)。TODO更新,这是合理的。

pdk.BitMapper{
Frame: "dist_miles",
Mapper: lfm,
Parsers: []pdk.Parser{pdk.FloatParser{}},
Fields: []int{fields["trip_distance"]},
},

这些相同的对象在JSON定义文件中表示:

{
"Fields": {
"Trip_distance": 10
},
"Mappers": [
{
"Name": "lfm0",
"Min": -0.5,
"Max": 3600.5,
"Res": 3600
}
],
"BitMappers": [
{
"Frame": "dist_miles",
"Mapper": {
"Name": "lfm0"
},
"Parsers": [
{"Name": "FloatParser"}
],
"Fields": "Trip_distance"
}
]
}

在这里,我们定义一个Mappers列表,每个Mappers都包含一个名称,我们将在稍后的BitMappers列表中用它来引用mapper。我们也可以使用Parsers进行此操作,但默认情况下可以使用一些不需要配置的简单解析器。 我们还有一个Fields列表,它只是一个字段名称(在源数据中)到列索引(在Pilosa中)的映射。 我们使用的BitMapper定义这些名字让人可读。

total_amount_dollars:在这里,我们再次使用舍入映射,因此每行代表一次行程的总成本,该成本将舍入映射为row ID。 BitMapper定义与前一个定义非常相似。

passenger_count:此列包含小整数,因此我们使用最简单的映射之一:列值是就是row ID。

1列(colums) --> 多字段(multiple field)

使用复合数据类型(如时间戳timestamp)时,有很多映射选项。 在这种情况下,我们希望看到有趣的周期性趋势。因此,我们希望以一种允许我们在分析过程中独立查看它们的方式对时间的循环分量进行编码。

我们通过将时间数据存储在每个时间戳的四个单独字段中来实现此目的:对于年year``月month``日day时间time各一个。 前三个是直接映射的。例如,乘坐的日期2015/06/24将在字段year的第2015行,字段month的第6行和字段day的第24行中设置。

我们可能会在几小时、几分钟和几秒钟内继续使用这种模式,但是我们在这里没有太多使用这种精度,所以我们使用bucketing方法。 也就是说,我们选择一个分辨率(30分钟),将日期划分为该大小的桶,并为每个桶创建一行。 因此,6:45 AM时间在time_of_day字段的第13行中设置了bit位(位图中)。

我们针对每个感兴趣的时间戳执行所有这些操作,一个用于上车时间,一个用于下车时间。 这为我们提供了两个时间戳的总字段:pickup_yearpickup_monthpickup_daypickup_timedrop_yeardrop_monthdrop_daydrop_time

多列(multiple colums) --> 1字段(field)

乘坐数据还包含地理定位数据:上车和下车的纬度和经度。 我们只是希望能够生成乘坐位置的粗略概述热图,因此我们使用网格映射。 我们将感兴趣的区域划分为经纬度空间中的100x100矩形网格,使用单个整数标记此网格中的每个单元格,并使用该整数作为row ID。

我们为每个感兴趣的位置做了所有这些,一个用于上车,一个用于下车。 这为两个位置提供了两个字段:pickup_grid_iddrop_grid_id

同样,位置数据有许多映射选项。 例如,我们可能会转换为不同的坐标系,应用投影或将位置聚合到实际区域(如邻域neighborhoods)。 这里,简单的方法就足够了。

复合映射(Complex mappings)

我们还期望寻找行程持续时间和速度的趋势,因此我们希望在导入过程中捕获此信息。

对于字段duration_minutes我们使用round((drop_timestamp - pickup_timestamp).minutes)计算row ID

对于字段speed_mph我们使用round(dist_miles / (drop_timestamp - pickup_timestamp).minutes)计算row ID`。

这些映射计算很简单,但由于它们需要对多列进行算术运算,因此在PDK中提供的基本映射器中捕获它们有点过于复杂。 相反,我们定义自定义映射器来完成工作:

durm := pdk.CustomMapper{
Func: func(fields ...interface{}) interface{} {
start := fields[0].(time.Time)
end := fields[1].(time.Time)
return end.Sub(start).Minutes()
},
Mapper: lfm,
}

导入处理

设计这个架构和映射后,我们可以使用PDK导入工具读取的JSON定义文件捕获它。

运行pdk taxi会根据此文件中的信息运行导入。 有关更多详细信息,请参阅PDK部分,或查看代码本身。

查询

现在我们可以运行一些示例查询。

可以在一个PQL调用对cab类型进行检索、排序、计数。

TopN(cab_type)
{"results":[[{"id":1,"count":1992943},{"id":0,"count":7057}]]}

可以使用类似的调用检索高流量位置ID。 这些ID对应于经纬度,可以从生成ID的映射中反算。

TopN(pickup_grid_id)
{"results":[[{"id":5060,"count":40620},{"id":4861,"count":38145},{"id":4962,"count":35268},...]]}

每个passenger_count(乘客计数)的total_amount(总金额)的平均值可以通过一些后处理来计算。我们使用少量的TopN调用来检索passenger_count的行程计数, 然后使用这些计数来计算平均值。

queries = ''
pcounts = range(10)
for i in pcounts:
queries += "TopN(Row(passenger_count=%d), total_amount_dollars)" % i
resp = requests.post(qurl, data=queries) average_amounts = []
for pcount, topn in zip(pcounts, resp.json()['results']):
wsum = sum([r['count'] * r['key'] for r in topn])
count = sum([r['count'] for r in topn])
average_amounts.append(float(wsum)/count)

注意,BSI-powered Sum查询现在提供了这种查询的替代方法。

有关更多示例和详细信息,请参阅此ipython notebook

Pilosa文档翻译(三)示例的更多相关文章

  1. 安卓图表引擎AChartEngine(三) - 示例源码折线图、饼图和柱状图

    折线图: package org.achartengine.chartdemo.demo.chart; import java.util.ArrayList; import java.util.Lis ...

  2. Pilosa文档翻译(一)导言、安装

    目录 导言 安装 安装在MacOS 使用HomeBrew 下载二进制文件 从源码构建 使用Docker 安装在Linux 下载二进制文件 从源码构建 使用Docker 接下来是什么? 导言 原文地址 ...

  3. Pilosa文档翻译(二)入门指南

    目录 开始 Pilosa 简单项目 创建架构(Create the Schema) 从CVS文件导入数据 做一些查询(Queries) 接下来做什么? Pilosa支持默认使用JSON的HTTP接口. ...

  4. Orchard官方文档翻译(三) 通过zip文件手动安装Orchard

    原文地址:http://docs.orchardproject.net/Documentation/Manually-installing-Orchard-zip-file 想要查看文档目录请用力点击 ...

  5. Log4j官方文档翻译(三、配置)

    之前的章节介绍了log4j的核心组件,本章将会通过配置文件介绍一下核心组建的配置. 主要在配置文件中配置log4j的日志级别,定义appender.layout等. log4j.properties是 ...

  6. Android官方文档翻译 三 1.1Creating an Android Project

    Creating an Android Project 创建一个Android项目 An Android project contains all the files that comprise th ...

  7. 安卓图表引擎AChartEngine(四) - 源码示例 嵌入Acitivity中的折线图

    前面几篇博客中都是调用ChartFactory.get***Intent()方法,本节讲的内容调用ChartFactory.get***View()方法,这个方法调用的结果可以嵌入到任何一个Activ ...

  8. 安卓图表引擎AChartEngine(二) - 示例源码概述和分析

    首先看一下示例中类之间的关系: 1. ChartDemo这个类是整个应用程序的入口,运行之后的效果显示一个list. 2. IDemoChart接口,这个接口定义了三个方法, getName()返回值 ...

  9. 【UNIX网络编程第三版】阅读笔记(一):代码环境搭建

    粗略的阅读过<TCP/IP详解>和<计算机网络(第五版)>后,开始啃这本<UNIX网络编程卷一:套接字联网API>,目前linux下的编程不算太了解,在阅读的过程中 ...

随机推荐

  1. 004 Java的一次面试题,学长列举

    据说是Java的面试题,感觉有些汗颜,所以决定研究一下. 1.线程.进程.协程的区别,他们怎么进行线程的交互? 2.k8s内的scheduler怎么做的,流程?跟etcd怎么交互? 3.hashmap ...

  2. 12306登录爬虫 cookies版本

    import requests import re import base64 cookies = None # 进入主页,保留cookies login_url = 'https://kyfw.12 ...

  3. 【LeetCode算法-9】Palindrome Number

    LeetCode第9题 Determine whether an integer is a palindrome. An integer is a palindrome when it reads t ...

  4. 002.Ceph安装部署

    一 前期准备 1.1 配置规格 节点 类型 IP CPU 内存 ceph-deploy 部署管理平台 172.24.8.71 2 C 4 G node1 Monitor OSD 172.24.8.72 ...

  5. AngularJS的Scope和Digest

    Angular是一个成熟和强大的JavaScript框架.它也是一个比较庞大的框架,在熟练掌握之前,需要领会它提出的很多新概念.很多Web开发人员涌向Angular,有不少人面临同样的障碍.Diges ...

  6. vsftpd中配置文件详解

    在vsftp服务器中,配置文件/etc/vsftpd/vsftpd.conf文件是配置的核心内容,其具体的配置信息详细情况如下: 1.默认配置: 1>允许匿名用户和本地用户登陆. anonymo ...

  7. xss小结-从xss平台搭建到csp规则

    0x00前言 xss是跨站脚本攻击,利用嵌入js代码达到‘控制’对方浏览器的作用,测试的时候我们是用alert(1)弹窗,而做CTF也好,实际中的漏洞利用也好一般是用xss获取管理员的cookie 0 ...

  8. 366. Fibonacci

    描述 查找斐波纳契数列中第 N 个数. 所谓的斐波纳契数列是指: 前2个数是 0 和 1 . 第 i 个数是第 i-1 个数和第i-2 个数的和. 斐波纳契数列的前10个数字是: 0, 1, 1, 2 ...

  9. 在UnrealEngine中用Custom节点实现马赛克效果

    参考这位大神的Shaderhttp://blog.csdn.net/noahzuo/article/details/51316015 //input BaseUV 屏幕UV //intput Tili ...

  10. HDU.4700.Flow(构造 最小割树)

    题目链接 \(Description\) 给定\(n\)以及\(n\)个点任意两点之间的最大流,求一张无向图满足给定条件. \(n\leq100\). \(Solution\) 有些类似最小割树. 我 ...