0%

出现次数相同的单词按照单词字母排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""Count words."""

def count_words(s, n):
"""Return the n most frequently occuring words in s."""
words = s.split(" ")
# TODO: Count the number of occurences of each word in s
counters = {}
for word in words:
if word in counters:
counters[word] += 1
else:
counters[word] = 1
# TODO: Sort the occurences in descending order (alphabetically in case of ties)
top = sorted(counters.iteritems(), key=lambda d:(-d[1],d[0]))
##lambda中"-"为反序,意思是先把单词出现次数从大到小排列,
##然后再以单词的字母从小到大排列
# TODO: Return the top n words as a list of tuples (<word>, <count>)
top_n = top[:n]##返回前n个单词
return top_n

def test_run():
"""Test count_words() with some inputs."""
print count_words("cat bat mat cat bat cat", 3)
print count_words("betty bought a bit of butter but the butter was bitter", 3)

if __name__ == "__main__":
test_run()

数据可视化用的highcharts.

运行结果:

upload successful

在html头部加上highcharts的cdn

1
2
3
4
<script type="text/javascript" src="http://cdn.hcharts.cn/jquery/jquery-1.8.3.min.js">
</script>
<script type="text/javascript" src="http://cdn.hcharts.cn/highcharts/highcharts.js">
</script>

数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
{
name: 'Female',
color: 'rgba(223,83,83,.5)',
data: [[161.2, 51.6],[167.5, 59.0],[159.5, 49.2],
[157.0, 63.0],[155.8, 53.6]...]
},
{
name: 'Male',
color: 'rgba(119,152,191,.5)',
data: [[174.0, 65.6],[175.3, 71.8],[193.5, 80.7],
[186.5, 72.6],[187.2, 78.8]...]
}
]

共有507个样本的身高与体重数据,标签分为男女。

====代码====

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
var Dot_1, Dot_2, temp_group_1, temp_group_2 = new Array;
var temp_data
var dataTotel = hasntlabel_data.length;
var TempDot = [0, 0];
random_dot();
initgroup();
k_means()
//主要代码
function init() { //初始化
group_1 = [];
group_2 = [];
console.log("init") for (var i = 0; i < dataTotel; i++) {
distance(Dot_1,hasntlabel_data[i])>distance(Dot_2,hasntlabel_data[i])?
group_2.push(hasntlabel_data[i]):group_1.push(hasntlabel_data[i]);
}
}
function random_dot() { //随机定两个点
Dot_1=[Math.random()*7,Math.random()*7]Dot_2=[Math.random()*7,Math.random()*7]
}
function k_means() {
console.log(group_1.length + ',' + group_2.length);
if (!group_1.length || !group_2.length) {
random_dot();
init();
k_means()
}
data_1_x_sum = 0;
data_1_y_sum = 0;
data_2_x_sum = 0;
data_2_y_sum = 0;
for (var i = 0; i < group_1.length; i++) {
data_1_x_sum += group_1[i][0];
data_1_y_sum += group_1[i][1];
}
for (i = 0; i < group_2.length; i++) {
data_2_x_sum += group_2[i][0];
data_2_y_sum += group_2[i][1];
}
TempDot[0] = Dot_1;
TempDot[1] = Dot_2;
Dot_1 = [(data_1_x_sum / group_1.length), (data_1_y_sum / group_1.length)];
Dot_2 = [(data_2_x_sum / group_2.length), (data_2_y_sum / group_2.length)];
if(Dot_1[0]==TempDot[0][0]&&Dot_1[1]==TempDot[0][1]
&&Dot_2[0]==TempDot[1][0]&&Dot_2[1]==TempDot[1][1]){
return true;
}
group_1 = [];
group_2 = [];
for (var i = 0; i < dataTotel; i++) {
distance(Dot_1,hasntlabel_data[i])>distance(Dot_2,hasntlabel_data[i])
?group_2.push(hasntlabel_data[i]):group_1.push(hasntlabel_data[i]);
}
k_means()
}

function distance(e, f) {
return Math.sqrt(Math.pow((f[0] - e[0]), 2) + Math.pow((f[1] - e[1]), 2));
}


当多次运行代码时,可以看到不同的结果

upload successful

对于K-means来说初始点对于结果的影响大,

且收敛太慢,算法复杂度高O(nkt),结果不一定是全局最优,只能保证局部最优。

之后会学习与使用K-means++之类的增强的算法来进行聚类分析。

命令行输入pip install selenium 安装selenium

测试:

1
2
3
4
5
6
7
from selenium import webdriver
browser = webdriver.Chrome()

browser.get("http://www.baidu.com")
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
browser.quit()

运行程序,可以看到程序自动打开了Chrome浏览器,进入baidu的网页,在id为kw的框(百度网页中的搜索框)中输入selenium,然后点击了id为su的按钮(百度网页中的搜索按钮)。

经过体验selenium非常好用,准备使用selenium+chrome来自动玩之前做的Flappy Bird。

加入了键盘响应事件:

1
2
3
4
5
6
document.onkeydown = function(event) {
if (gamestate == 1 || gamestate == -1) {
start()
}
tap()
}

将图片插入后,页面改成如下:

希望以某种方式获取鸟与柱子的实时数据。因此使用NodeJS进行获取。

在网页中加入js:

1
2
3
var upload = setInterval(function() {
$.get("http://localhost:8000/?x-distance=" + (300 - pillarX - 50) + "&y-distance=" + (pillarupH + 100 - startY))
}, 20)

可以每20毫秒以get的方式请求一次服务器,将相关的数据上传。因为是本机搭建服务器因此无需担心由于网络环境造成的丢包与延迟等情况。

NodeJS代码如下

1
2
3
4
5
6
7
8
9
10
var http = require('http');
var url = require('url');
var util = require('util');

http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(util.inspect(url.parse(req.url, true)));
var arg = url.parse(req.url, true).query;
console.log(JSON.stringify(arg))
}).listen(8000);

直接在本机8000端口建立服务器,并获得前端的get请求。

运行效果如下

能获得当前数据信息,之后考虑如何让电脑学会玩这个小游戏

效果:

upload successful
upload successful
upload successful

主要使用了定时器,做出速度与加速度的效果,并配合改变Bird的角度,达到想要的效果。

检测游戏是否失败靠检测鸟的上下左右点是否在柱子内或者在地图外。

Html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title></title>
</head>

<body>
<div id="start" onclick="start()">start</div>
<div id="startbackground"></div>
<div id="warp" onclick="tap()">
<div id="bird"></div>
<div id="pillar-up"></div>
<div id="pillar-down"></div>
<div id="background"></div>
</div>
</body>
<style>
body{
height: 500px;
width: 300px;
}
#start {
position: fixed;
z-index: 999;
top: 250px;
left: 150px;
border: 2px solid#eee;
background-color: #888;
height: 50px;
width: 150px;
margin-left: -75px;
text-align: center;
line-height: 50px;
font-size: 30px;
}

#startbackground {
position: fixed;
z-index: 888;
height: 500px;
width: 300px;
background-color: black;
opacity: 0.5;
}

#warp {
position: relative;
border: 1px solid#000;
height: 500px;
width: 300px;
}

#bird {
position: absolute;
height: 20px;/*鸟高 */
width: 30px;/*鸟长*/
background-color: red;
top: 100px;
left: 50px;
transform: rotate(0deg);
-ms-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
-o-transform: rotate(0deg);
}

#pillar-up {
position: absolute;
height: 0;
width: 50px;
background-color: green;
top: 0;
right: -50px;
}

#pillar-down {
position: absolute;
height: 0;
width: 50px;
background-color: green;
bottom: 0;
right: -50px;
}
</style>
<script>
var startX = 50;//角色距离左边x坐标
var startY = 100; //角色开始高度
var maxTop = 480; //地图高度500 角色碰撞高度20
var v_y = 0; //向下运动为正,每1个时间单位向下移动v_y个像素
var v_x = 2; //水平相对运动速度(实际上没有运动)
var g = 0.7; //重力加速度,每个时间单位速度向下加这么多
var Bird = document.getElementById("bird");
var startBtn = document.getElementById("start");
var pillarup = document.getElementById("pillar-up");
var pillardown = document.getElementById("pillar-down");
var pillarX = -50; //柱子初始位置
var gamestate = 0;
var pillarstate = 0;
var scorecount = 0;
var score = 0;//分数
function start() {
if (gamestate == 1) {
window.location.reload()
} else {
main();
startBtn.style.display = "none";
document.getElementById("startbackground").style.display = "none";
}
}

function tap() {
v_y = -12;
}

function main() {
var birdtimer = setInterval(function() {
if (startY > maxTop || startY < 0) { //角色碰撞上下边界
gameover();
}
if ((pillarX>220&&pillarX<280&&startY<pillarupH)||(pillarX>220&&pillarX<280&&startY>(200+ pillarupH))){
gameover();
}
startY += v_y;
v_y += g;
deg = Math.atan(v_y / v_x) / 1.57 * 60;

Bird.style.webkitTransform = "rotate(" + deg + "deg)";
Bird.style.MozTransform = "rotate(" + deg + "deg)";
Bird.style.msTransform = "rotate(" + deg + "deg)";
Bird.style.OTransform = "rotate(" + deg + "deg)";
Bird.style.transform = "rotate(" + deg + "deg)";
Bird.style.top = startY + "px";
}, 20) //一次时间单位
var pillartimer = setInterval(function() {
if (pillarstate == 0) {
pillarup.style.right = "-50px";
pillardown.style.right = "-50px";
pillarupH = 300 * Math.random();
pillarup.style.height = pillarupH + "px";
pillardown.style.height = (300 - pillarupH) + "px";
pillarstate = 1;
}
if (pillarX >= 350) {
pillarX = -50;
pillarstate = 0;
scorecount = 0;
}
if(pillarX >= 250&&scorecount == 0){
score++;
scorecount = 1;
}
pillarX += v_x;
pillarup.style.right = pillarX - 50 + "px";
pillardown.style.right = pillarX - 50 + "px";
}, 20) //一次时间单位
function gameover() {
clearTimeout(birdtimer);
clearTimeout(pillartimer);
document.getElementById("startbackground").style.display = "block"
startBtn.innerText = '失败,分数为'+ score +',重新开始?';
startBtn.style.display = 'block';
startBtn.style.fontSize = '11px';
gamestate = 1;
}
}
</script>

</html>

当给出一组未经标记的数据,让程序自己去对其数据结构进行分析与分类(加上标签),就是无监督学习。

例如聚类就是无监督学习中的一个典型例子。

K-means算法

K-means算法(K均值算法)是现在使用最为广泛的聚类算法。

使用步骤如下:

首先随机选择两个点(分成两个聚类),称其为聚类中心;

K-means是一个迭代算法,它将不断进行簇分配与聚类中心移动。

upload successful

1、簇分配

根据数据集中的点距离哪个聚类中心更近从而将其分为两类,例如:

upload successful

2、移动聚类中心

分别将之前的两个聚类中心移动到它那个聚类的均值处。如图:

upload successful

两个中心都移动到均值处。

upload successful

之后再次重复第一步簇分配,重新根据数据点距离聚类中心的距离重新分类,之后再次移动聚类中心……

一直迭代下去,直到聚类中心不再移动,簇分配也不再变化,此时称此时K-means已经收敛了。


以较科学的方式描述K-means

upload successful

K为簇数,即期望将未标记的数据分为几类的值;

具体算法:

upload successful

1、随机初始化K个聚类中心Mu1,Mu2……MuK

重复以下的事情:

2、将x中的每个值根据距离Mu的距离分类(簇分配),距离公式如下:

\[min_k \left | x^{(i)}-\mu_k \right |^2\]

3、对于每个聚类中心重新规划在其对应聚类的平均值处。


K-means的实际用途

例如市场细分:

upload successful

对于服装场来说,如果要将调查到的如上数据的人群分为大中小三个聚类,肉眼从图上看无法分开这三种尺寸。

使用K-means可以将其顺利分为3个聚类,

upload successful

由此可以看到K均值的确可以比人更好的解决一些问题。

当发现测试算法时预测的结果有非常大的误差时,有以下几种方法解决问题:

  1. 使用更大的训练集

  2. 使用更少的特征值

  3. 使用更多的特征值

  4. 使用更高次幂的特征值

  5. 减少正则化参数

  6. 增加正则化参数

判断如何选择方法不应该"凭感觉"选择,而应该科学地使用机器学习诊断法进行判断,以更省时间,更有意义地改进算法.

过拟合与欠拟合

upload successful

(图为过拟合)

过拟合与欠拟合都将不能很好的匹配新的数据.

方法:将所有数据分为训练集与测试集.比例可以使用7:3.

当训练集的\(J(\theta )\)很小,测试集的\(J(\theta )\)很大时,此时即为过拟合的情况.

\[ J_{test}(\theta )=\frac{1}{2m_{test}}\sum_{i=1}^{m_{test}}(h_{\theta}(x_{test}^{i})-y_{test}^{i})^{2}\]

以上公式即为线性回归测试集误差计算公式;

\[ J_{test}(\theta )=-\frac{1}{m_{test}}\sum_{i=1}^{m_{test}}y_{test}^{i}\log h_{\theta }(x_{test}^{i})+(1-y_{test}^{i})\log h_{\theta }(x_{test}^{i})\]

以上公式为逻辑回归测试误差公式;

可以看到其实误差公式与代价函数的计算是一致的.

通过以上计算公式可以量化测试集测试训练集的训练结果.


确定多项式的次数

已经知道,当多项式的次数过高的时候,会发生过拟合现象.如何根据数据来确定多项式次数d呢?

与之前加上验证集不同的是,将数据集分成三部分:训练集,测试集,交叉验证级.

选择不同的多项式次数分别对验证集进行计算,

\[ 1. h_{\theta }(x)=\theta _{0}+\theta _{1}x\\2. h_{\theta }(x)=\theta _{0}+\theta _{1}x+\theta _{2}x^{2}\\3. h_{\theta }(x)=\theta _{0}+\theta _{1}x+...+\theta _{3}x^{3}\\\vdots \\10. h_{theta }(x)=\theta _{0}+\theta _{1}x+...+\theta _{10}x^{10}\]

得到各自的\(J_{cv}(\theta )\),由此可以选择出最合适的多项式次数.


因此,选择算法使用验证集进行模型选择,使用测试集来评价模型的好坏,这样就能找到合适的模型.

偏差(bias)与方差(variance)

upload successful

具体来说,欠拟合是高偏差情况,过拟合是高方差情况.

分别对训练集和验证集的误差情况与多项式次数进行画图

upload successful

可以看到当项数次数过少时,错误相当的大,此时是高偏差情况;当项数次数过多时,训练集的误差变得非常小,呈现出过拟合的情况.但是交叉验证集的错误将急剧上升,此时的情况是高方差.

直接描述就是:训练集误差小验证集误差大时,就是过拟合-高方差情况;

选择合适的正则化参数lambda

改变lambda时,训练集\(J_{train}\)与交叉验证集\(J_{cv}\)都会发生一定的变化.

当参数过多过大时,后面的正则化项的值就会很大,因而导致$ J()$也变大,从而使这个预测方程得到更多的惩罚,使其像参数变量尽量小的地步靠拢。对正则化过程有影响的是lambda,当lambda数值大的时候,函数收到的惩罚更多,会更快地减少参数数值;当lambda为0的时候,函数则不会对参数进行正则化处理。

如图所示

upload successful

当lambda过大时,会处于高偏差情况,\(J_{train}\)的值会很大,lambda值过小时会处于过拟合情况.

因此,上图的左端对应着高方差,右端对应着高偏差问题,

学习曲线

用于判断算法的学习情况.可以判断此时处于高方差高偏差情况以对于算法进行修改.

绘出\(J_{train}(\theta )\)\(J_{cv}(\theta )\)的图像.

upload successful

上图为训练集大小与两个J的对应情况.

当训练集过大时,\(J_{train}\)越来越难对与过大的训练集做出良好的拟合,因此在图上表现出m越大error越多.

相反,当训练集大的时候,\(J_{cv}\)更能获得更好的泛化,即对新样品的适应更好,因此可以得到上述曲线.

如果不使用多项式拟合而直接用一条直线,那么数据集的大小再大也于事无补,无法更加接近实际情况.此时\(J_{train}\)\(J_{cv}\)会很接近,

upload successful

这种情况便是高偏差情况.

反之,如果项数很大的话,学习曲线会如图所示:

upload successful

\(J_{train}\)\(J_{cv}\)间会有很大的差距,这种情况是高方差.



回到最开始的情况

  1. 使用更大的训练集可以对于高方差的情况有所帮助(将学习曲线画出来可以看出)

  2. 使用更少的特征值可以修复一定的高方差情况.

  3. 使用更多的特征值可以修复高偏差情况

  4. 使用更高次幂的特征值可以修复高偏差情况

  5. 减少正则化参数可以修复高偏差

  6. 增加正则化参数可以修复高方差

目标:http://yun.kujiale.com/api/openfps?query=beijing&start=1&num=50 中的所有户型数据,将其户型图片下载到本地.

目标中的数据以json形式存在,因此使用json.loads.

全部代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#coding=utf-8
#encoding=utf-8
import re
import requests
import json
import urllib
def valifile(name):
rstr = r"[/\:*?"<>|]" # ';/:*?"<>|';
valiname = re.sub(rstr, "", name)
return valiname

def getkjlimages():
for i in range(0,1000000):
print(i)
i = str(i*6)
surl = ';http://yun.kujiale.com/api/openfps?query=beijing&amp;start=';+i+';&amp;num=6';
ret=requests.get(url=surl)
ret.encoding=';utf-8';
kjl = json.loads(ret.text)
for j in range(0,49):
itmes = kjl[';obsExFps';][j]
name = json.dumps(itmes[';name';])[1:-1].decode("unicode-escape")
src = json.dumps(itmes[';pics';])[1:-1]
##name和src后面加了[1:-1]的原因是json数据中包含了前后两个引号,要将其去掉
print(';img_src=';+json.dumps(itmes[';pics';]))
img = urllib.urlopen(src).read()
f = file(';imgs/';+validateTitle(name)+';.jpg';,"wb")
f.write(img)
f.close
getkjlimages()

其中,json.dumps出来的字符串为 uxxxx形式,需要用decode("unicode-escape")进行转换成中文.另外,由于爬下来的name要直接用于文件名,其中有""等符号,因此需要将name合法化.

1
2
3
4
def valifile(name):
rstr = r"[/\:*?"<>|]" # ';/:*?"<>|';
valiname = re.sub(rstr, "", name)
return valiname

通过以上valifile可以将文件名在windows下合法化.

运行结果:

upload successful

目标网站:http://jc.bjmemc.com.cn/IndexAirReport/AirDailyReport.aspx

需要将所有数据爬下放到数据库中.中途遇到了各种编码问题,因此使用了requests获取页面.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#coding=utf-8
#encoding=utf-8
import re
import requests
import MySQLdb
import hashlib
from pyquery import PyQuery as pyq
def md5(text):
m = hashlib.md5()
m.update(text.encode('utf-8'))
return m.hexdigest()

def getandupload():
conn=MySQLdb.connect(host='localhost',user='root',passwd='',port=3306,charset='utf8')
cur=conn.cursor()
conn.select_db('pytest')
ret=requests.get(url="http://jc.bjmemc.com.cn/IndexAirReport/AirDailyReport.aspx")
doc=pyq(ret.text)
date = re.findall(r'(w*[0-9]+)w*',doc.find('#Label0').text())
#正则,将数字提取出来(为了将yyyy年mm月dd日改成yyyy/mm/dd)
date = date[0]+'/'+date[1]+'/'+date[2]
table = pyq(pyq(doc.find('#marqueebox')).find('table'))
stations = re.findall(r'<td style="width:126px">(.+?)</td>',table.html())
aqis = re.findall(r'<td style="width:90px">(.+?)</td>',table.html())
#通过正则将html代码中需要的数据提取出来并存在数组中.
for i in range(0, 35):
value=[md5(date+stations[i][1:]),date,stations[i][1:],aqis[2*i]]
cur.execute('insert ignore into aqi_bjmemc values(%s,%s,%s,%s)',value)
#设置了md5id为主键,因此当传重复数据的时候md5id重复,避免了重复上传数据.
conn.commit()
cur.close()
conn.close()
getandupload()

某数据写入数据表时,其中一个字段是time,具体内容是从00:00一直到23:30。因此打算用python对csv文件进行逐行处理,新建time列,其中的数据按照半小时递增。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import os
import time
#对于时间进行处理需要引入time

def importcsv(filename,outfilename):
print(filename)
filenamerow = filename.split('-')
filenamerow[2] = filenamerow[2][:-4]
date = filenamerow[1]
type = filenamerow[2]
lctime = 1325347200.0
#设定一个时间戳,对应的是某一天的00:00
g=file(type+ '-' +date+ '.csv','w+')
with open(filename, 'r') as f:
line = f.readline()
while line:
line = line.replace('n','')
line = line.replace('r','')
line = line + ',,'
row = line.split(',')
row[2] = row[0]
x = time.localtime(lctime)
#对时间戳进行处理,得到
#time.struct_time(tm_year=2012, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=1, tm_isdst=0)
row[1] = time.strftime('%H:%M',x)
#对以上的时间进行格式化并写入第二列。格式为HH:MM
row[0] = date
for i in range(0, len(row)-1):
row[i] = row[i] + ','
g.writelines(row)
g.writelines('n')
lctime = lctime + 1800.0
#时间戳加上1800,相当于加上了1800s = 30min
line = f.readline()
g.close()

def ListFiles():
for i in os.listdir(data_folder):
if i.endswith('.csv'):
importcsv(i,i)

python中时间戳都是以s为单位,且格式为浮点