Project 2 : 北京地铁数据处理及路径探寻

完成北京地铁路线搜寻

使用搜寻策略完成如下项目:接收两个北京地铁站站点,得到两个站点间的优化路径。
Please using the search policy to implement an agent. This agent receives two input, one is @param start station and the other is @param destination. Your agent should give the optimal route based on Beijing Subway system.

思路:

一、首先我们需要获取数据:
数据源大概分为四类:

  1. 公开开放的数据集,如《统计年鉴》等。
  2. 可以爬取的网页数据,如,网站内容或者APP内容等,注意robots协议。
  3. 传感器数据,如无人驾驶汽车雷达采集信息等,一般是物理信息。
  4. 日志信息,用于统计用户的操作,可以前端埋点收集或者后端脚本收集及统计,以统计网站的访问情况及瓶颈等,如网站上一个商品的点击率数据。

二、数据清洗:

  1. 使用正则表达式,获取站点信息和路线信息
  2. 将以上信息整理成可使用的数据结构

三、完成路径探寻:

  1. 广度优先搜索、宽度优先搜索

Python爬虫工具

爬虫工具有两类:一类为可视化工具,以八爪鱼和火车头为代表。特点速度快,模板多,但不全面不够个性化。
另一类为Python爬虫工具,Beautiful Soup and Selenium;Selenium主要用于模拟人的操作。两者均可以自定义爬取内容。

要想得到北京市地铁站及线路信息,要想直接从官网获取,并不容易,需要大量的使用正则表达式。
这里我们可以从高德地图等网站,找到地铁的 json 数据直接调用,json 格式是标准的字典格式。
高德地铁图网站:http://map.amap.com/subway/index.html
subway:http://map.amap.com/service/subway_1599542108002&srhdata=1100_drw_beijing.json
在这里插入图片描述
在这里插入图片描述
可以看到 json字符串 是标准的字典格式。直接爬取此网站即可获得地铁站及线路信息。

Re正则表达式

import re
r"" 表示为⼀个正则表达式 
pattern = r"[1-9][0-9]{4,}"
m = re.findall(pattern, '这是我的QQ号781504542,第二个qq号:10054422288')
print(m) if m is not None else print('None')
字符描述
\将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\”匹配“\”而“(”则匹配“(”。
^匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
*匹配前面的子表达式零次或多次。例如,zo*能匹配“z”以及“zoo”。* 等价于{0,}。
+匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
?匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。
{n}n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,}n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m}m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
?当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。
.匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(.
(pattern)匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用 0… 9属性。要匹配圆括号字符,请使用“(”或“)”。
(?:pattern)匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(
(?=pattern)正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95
(?!pattern)正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95
(?<=pattern)反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“(?<=95
(?<!pattern)反向否定预查,与正向否定预查类拟,只是方向相反。例如“(?<!95
x|y匹配x或y。例如, “z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。
[xyz]字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
[ ^xyz ]负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。
[a-z]字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
[ ^a-z ]负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。
\b匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
\B匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\cx匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
\d匹配一个数字字符。等价于[0-9]。
\D匹配一个非数字字符。等价于[^0-9]。
\f匹配一个换页符。等价于\x0c和\cL。
\n匹配一个换行符。等价于\x0a和\cJ。
\r匹配一个回车符。等价于\x0d和\cM。
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S匹配任何非空白字符。等价于[^ \f\n\r\t\v]。
\t匹配一个制表符。等价于\x09和\cI。
\v匹配一个垂直制表符。等价于\x0b和\cK。
\w匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。
\W匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
\xn匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。.
\num匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。
\n标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
\nm标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。
\nml如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
\un匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。

广度优先搜索(BFS)、宽度优先搜索(DFS)

BFS使用队列的思想,先进先出;DFS使用栈的思想,后进先出。
BFS:宽度优先,首先搜索全部子节点,完成后搜索全部子节点的全部子节点。
在这里插入图片描述
DFS:深度优先,一条道走到黑,先在左节点上全部走完,然后回退。
在这里插入图片描述
本题中:由于每条线路的地铁站众多,但每一个地铁站的相邻节点最多只有四个,所以 DFS 的时间与空间复杂度均高与 BFS 。

流程

一、数据加载:

import requests
import re
r = requests.get('http://map.amap.com/service/subway?_1469083453978&srhdata=1100_drw_beijing.json')
print('文本编码:',r.encoding)
print('相应状态码:',r.status_code)
print('字符串方式响应体:',r.text)
#使用json解析
import json
import pandas as pd
bj_s = json.loads(r.text)
bjs = pd.DataFrame(bj_s)
bjs.head()

在这里插入图片描述

二、数据预处理

#获取地铁线路信息
#获取站点名称n
#获取经纬度信息sl
pattern_kn = r'"kn":"(\w+)"|"n":"(\w+)"'
pattern_n = r'"n":"(\w+)"'
pattern_sl = r'"sl":"(\d+\.\d+,\d+\.\d+)"'
line = re.findall(pattern_kn,r.text)
#print(line)
name = re.findall(pattern_n,r.text)
#print(name)
location = re.findall(pattern_sl,r.text)
#print(location)

构建站点名称和经纬度的字典

#构建站点名称和经纬度的字典
station_info = {}
for i in range(len(name)):
    station_info[name[i]] = tuple(map(float,location[i].split(',')))
#print(station_info.values())
station_df = pd.DataFrame(station_info)
station_df2 = pd.DataFrame(station_df.values.T,index = station_df.columns,columns = station_df.index)
print(station_df2.head)
line_info = {}
li = list()
for j in line:
    if j[0] =='':
        li.append(j[1])
    else:
        line_info[j[0]] = li
        li = list()
print(line_info)

在这里插入图片描述
建立邻接链表dic

#建立邻接链表dic
line_list = list(line_info)
#print(line_list)
already_check = list() 
connection = {}
for g in line_list:
    for h in range(len(line_info[g])):
        if line_info[g][h] not in already_check:
            if 0<h<len(line_info[g])-1:
                connection[line_info[g][h]] = [line_info[g][h-1],line_info[g][h+1]]
            if h == 0:
                connection[line_info[g][h]] = [line_info[g][h+1]]
            if h == len(line_info[g])-1:
                connection[line_info[g][h]] = [line_info[g][h-1]]
            already_check.append(line_info[g][h])
        else:
            if 0<h<len(line_info[g])-1:
                connection[line_info[g][h]] += [line_info[g][h-1],line_info[g][h+1]]
            if h == 0:
                connection[line_info[g][h]].append(line_info[g][h+1])
            if h == len(line_info[g])-1:
                connection[line_info[g][h]].append(line_info[g][h-1])

print(connection['宋家庄'])

三、绘制地铁分布图

import networkx as nx
import matplotlib.pyplot as plt
from pylab import mpl
 # 指定默认字体,解决不显示中文
plt.rcParams['font.sans-serif'] = ['SimHei']                         
plt.rcParams['axes.unicode_minus'] = False  

plt.figure(figsize=(30,30))
station_info.keys()
#print(station_info.keys())
station_graph = nx.Graph()
station_graph.add_nodes_from(list(station_info.keys()))
nx.draw(station_graph,station_info,with_labels=True,nodes_size=30)

plt.figure(figsize=(30,30))
station_network = nx.Graph(connection)
nx.draw(station_network,station_info,with_labels=True,node_size=30)

在这里插入图片描述

四、使用广度优先算法得到路径

#定义距离函数
def search_path (graph,start,destination):
    
    already_check = []#存放检查过的点
    dic = {}#存放检查过的node的prestation
    expand = [start]#存放需要检查的点
    if start not in graph:
        print(0)
        return 
    if destination not in graph:
        return 
    if graph[start] == []:
        print(1)
        return 
    while expand:
        node = expand.pop(0)
        
        if node == destination: 
            return path(start,destination,dic,the_path=[])
            
        
        if node in already_check:continue
        else:
            neighbor = []
            already_check.append(node)
            neighbor = graph[node]
            for i in neighbor:
                if i in already_check: continue
                dic[i] = node
            #print(dic)
            expand += neighbor
            continue
        
def path (initial,value,dic,the_path=[]):
    if value == initial:
        the_path.append(initial)
        return the_path
    value1 = dic[value]
    the_path.append(value)
    return path(initial,value1,dic,the_path)

list = []
list = search_path(connection,'天宫院','宋家庄')
list.reverse()
print(list)

[‘天宫院’, ‘生物医药基地’, ‘义和庄’, ‘黄村火车站’, ‘黄村西大街’, ‘清源路’, ‘枣园’, ‘高米店南’, ‘高米店北’, ‘西红门’, ‘新宫’, ‘公益西桥’, ‘角门西’, ‘角门东’, ‘大红门’, ‘石榴庄’, ‘宋家庄’]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值