
暂无个人介绍
这篇主要是总结JavaScript常见简单实用的特效,主要从代码量短、简单实用几个方面进行叙述。其中特效包括: 1.鼠标悬停图片切换查看器; 2.鼠标移动图片放大; 3.鼠标移动切换内容; 4.贵财下拉菜单案例; 5.JS图片放大镜功能-类似淘宝; 6.下一页翻页跳转功能。 下载地址: 希望文章对你有所帮助,尤其是学习前端JavaScript的同学。 一. 鼠标悬停图片切换查看器 代码如下所示,通过JavaScript函数showDaTu显示大图,重点是在<img>中调用onmouseover鼠标函数,然后通过document.getElementById函数实现换图。 <html> <head> <title> JavaScript 图片切换 </title> </head> <body> <script> function showDaTu(src){ document.getElementById("defaultImg").src=src; } </script> <img src="wall1.jpg" id="defaultImg"> <br><br><br> <img src='wall_s1.jpg' onmouseover="showDaTu('wall1.jpg')"> <img src='wall_s2.jpg' onmouseover="showDaTu('wall2.jpg')"> <img src='wall_s3.jpg' onmouseover="showDaTu('wall3.jpg')"> <img src='wall_s4.jpg' onmouseover="showDaTu('wall4.jpg')"> <br>因图片较大,请等待图片加载完成……然后鼠标放小图上就会切换了。 </body> </html> 运行结果如下图所示: 二. 鼠标移动图片放大 该部分参考:http://blog.csdn.net/u014175572/article/details/51535768 CSS3的transform:scale()可以实现按比例放大或者缩小功能。 CSS3的transition允许CSS的属性值在一定的时间区间内平滑地过渡。这种效果可以在鼠标单击、获得焦点、被点击或对元素任何改变中触发,并圆滑地以动画效果改变CSS的属性值。 代码如下所示: <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> div{ width: 300px; height: 300px; border: #000 solid 1px; margin: 50px auto; overflow: hidden; } div img{ cursor: pointer; transition: all 0.6s; } div img:hover{ transform: scale(1.4); } </style> </head> <body> <div> <img src="focus.jpg" /> </div> </body> </html> 效果如下图所示,包括缩放前后的对比。 transition: all 0.6s;表示所有的属性变化在0.6s的时间段内完成。 transform: scale(1.4);表示在鼠标放到图片上的时候图片按比例放大1.4倍。 PS:这部分代码参考博主"简单就是美",推荐大家可以去学习下,非常不错。 三. 鼠标移动内容切换 这段代码参考文章:http://blog.csdn.net/hill_kinsham/article/details/52448668 重点说一下关键功能的几个函数。 1.onmouseover=" change('zs', this) " 函数的功能是鼠标移动到目标区域时,响应函数。这里的'zs'用id与后面要变更的区域绑定。this的功能不太了解,效果是改变当前的值。 2.onmouseout="change2(this)";函数的功能是鼠标移开目标区域时,响应函数。 3.display. display 属性规定元素应该生成的框的类型。 none 此元素不会被显示。block 此元素将显示为块级元素,此元素前后会带有换行符。inline 默认。此元素会被显示为内联元素,元素前后没有换行符。 4.用<ul>时,去掉行号,并把它放到最左边。 list-style-type: none; <html> <head> <meta charset="UTF-8"> <style> body{ font-size: 12px; } .div1{ width: 126px; height: 156px; /* background-color: peachpuff;*/ } .navi{ width: 21px; height: 156px; /* background-color: yellowgreen;*/ float: left; } .navi ul{ padding: 0px; margin-left: 0px; margin-top: 0px; } .navi ul li{ list-style-type: none; width: 21px; height: 43px; margin-top: 4px; text-align: center; padding-top: 5px; background-color: silver; } .zs, .rz,.ky{ width: 101px; margin-left: 4px; height: 156px; margin-top: 0px; /*background-color: rosybrown;*/ float: left; } .zs ul,.rz ul,.ky ul{ padding: 0px; margin-left: 0px; margin-top: 3px; float: left; } .zs ul li,.rz ul li,.ky ul li{ list-style-type: none; line-height: 19px; } .rz,.ky{ display: none; } </style> <title>souhu</title> <script language="JavaScript"> <!-- function change(val,obj) { obj.style.backgroundColor="#FFC12D"; if(val=='zs'){ zs.style.display='block'; rz.style.display='none'; ky.style.display='none'; }else if(val=='rz'){ ky.style.display='none'; zs.style.display='none'; rz.style.display='block'; }else if(val=='ky'){ ky.style.display='block'; zs.style.display='none'; rz.style.display='none'; } } function change2(val) { val.style.backgroundColor="silver"; } //--> </script> </head> <body> <div class="div1"> <div class="navi"> <ul> <li onmouseover="change('zs',this)" onmouseout="change2(this)">招生</li> <li onmouseover="change('rz',this)" onmouseout="change2(this)">热招</li> <li onmouseover="change('ky',this)" onmouseout="change2(this)">考研</li> </ul> </div> <div id="zs" class="zs"> <ul> <li><a href="#">招生招生招生招生</a></li> <li><a href="#">招生招生招生招生</a></li> <li><a href="#">招生招生招生招生</a></li> <li><a href="#">招生招生招生招生</a></li> <li><a href="#">招生招生招生招生</a></li> <li><a href="#">招生招生招生招生</a></li> </ul> </div> <div id="rz" class="rz" > <ul> <li><a href="#">热招热招热招热招</a></li> <li><a href="#">热招热招热招热招</a></li> <li><a href="#">热招热招热招热招</a></li> <li><a href="#">热招热招热招热招</a></li> <li><a href="#">热招热招热招热招</a></li> <li><a href="#">热招热招热招热招</a></li> </ul> </div> <div id="ky" class="ky" > <ul> <li><a href="#">考研考研考研考研</a></li> <li><a href="#">考研考研考研考研</a></li> <li><a href="#">考研考研考研考研</a></li> <li><a href="#">考研考研考研考研</a></li> <li><a href="#">考研考研考研考研</a></li> <li><a href="#">考研考研考研考研</a></li> </ul> </div> <div></div> <div></div> </div> </body> </html> 运行结果如下图所示: 四. 贵财下拉菜单案例 这是模仿贵州财经大学主页下拉菜单的一段代码,非常有用。希望对你有所帮助,感谢我的学生。 详见下载地址:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>下菜单实现</title> <style> body{ width:100%; padding:0px; margin:0px; } #layout{ margin-top:10px; padding:0px; width:1024px; margin-left:auto; margin-right:auto; } #top{ width:1024px;; height:59px; } #top #logo{ float:left; margin-bottom:0px; } #top #second{ float:left; margin-left:160px; margin-top:20px; padding:0px; } #top #second li{ font-size: 12px; font-weight: bolder; margin-right: 20px; list-style-type: none; float: left; font-family: "微软雅黑"; } #top #second li a{ text-decoration:none; color:gray; } #top #second li a:hover{ color:red; } #top #third{ float: left; border::gray 1px solid; border: 2px solid #FFF; background-color:gray; margin-left:53px; margin-top:10px; padding-top:8px; padding-bottom:8px; padding-left:20px; padding-right:5px; border-radius:8px; } #top #third li{ float:left; margin-right:10px; list-style-type:none; font-size:12px; } #top #third li a{ color:white; text-decoration:none; } #top #third li a:hover{ text-decoration:underline; } #menu{ width: 2000px; height: 50px; background-color: #313b4d; padding-top: 0px; padding-left: 460px; padding-bottom: 0px; margin-left: -460px; z-index: 20; } #menu #first{ position: relative; width:100%; margin-left:10px; padding:0px; } #menu #first li{ float: left; list-style-type: none; font-size: 14px; margin-right: 40px; margin-top: 15px; margin-bottom: 0px; padding-bottom: 15px; font-family: "微软雅黑"; } #tit1:hover#tit1:hover,#tit2:hover,#tit3:hover,#tit4:hover,#tit5:hover,#tit6:hover,#tit7:hover{ background-image: url(backgroundimg.png); background-repeat: no-repeat; background-position: center bottom; } #tit1:hover #one,#tit2:hover #two,#tit3:hover #three,#tit4:hover #four,#tit5:hover #five,#tit6:hover #six,#tit7:hover #seven{ display:inline; } #tit1 a, #tit2 a, #tit3 a,#tit4 a,#tit5 a,#tit6 a,#tit7 a{ color:white; text-decoration:none; font-weight:bolder; padding:0px; margin:0px; } #one,#two,#three,#four,#five,#six,#seven{ position: absolute; background-color:white; top: 50px; left:-460px; width: 1920px; padding-top: 25px; padding-left: 0px; padding-bottom: 25px; display: none; margin-right:0px; } #one img,#two img,#three img,#four img,#five img,#six img,#seven img{ float:left; margin-right:50px; margin-left:460px; } #one ul,#two ul,#three ul,#four ul,#five ul,#six ul,#seven ul{ font-family: "微软雅黑"; margin-top:10px; width:1200px; } #one li a,#two li a,#three li a,#four li a,#five li a,#six li a,#seven li a{ color:#313b4d; font-weight:normal; margin-right:5px; } #one li a:hover,#two li a:hover,#three li a:hover,#four li a:hover,#five li a:hover,#six li a:hover,#seven li a:hover{ color: #AD0000; } form{ float:left; width:200px; border:white solid 2px; border-radius:8px;; padding-left:5px; padding-bottom:5px; margin-top:10px; margin-left:133px; } #search{ font-family: Arial, Helvetica, sans-serif; color: gray; font-size: 12px; border: none; padding:0px; margin-left:5px; margin-top: 5px; margin-bottom:3px; background-color: transparent; letter-spacing:1px; } #search_img{ margin-top:17px; margin-bottom:3px; margin-left:-30px; } #bg{ padding:0px; width:100%; height:520px; background-color:black; margin-top:0px; } p{ font-family:"微软雅黑" font-size: 16px; font-weight: bolder; text-align:center; color: gray; margin-top:50px; border-top-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-bottom-style: solid; border-top-color: #333; border-right-color: #333; border-bottom-color: #333; border-left-color: #333; } </style> </head> <body> <div id="layout"> <div id="top"> <img id="logo" src="gufe_logo.png"> <ul id="second"> <li><a href="#">领导信箱</a></li> <li><a href="#">信息公开</a></li> <li><a href="#">数字贵财</a></li> <li><a href="#">邮箱</a></li> <li><a href="#">English</a></li> </ul> <ul id="third"> <li><a href="#">学生</a></li> <li><a href="#">教职工</a></li> <li><a href="#">校友</a></li> <li><a href="#">考生/访客</a></li> </ul> </div> <div id="menu"> <ul id="first"> <li id="tit1"><a href="#">贵财概况</a> <div id="one"> <img src="gaikuo_img.jpg"/> <ul > <li><a href="#">学校简介</a></li> <li><a href="#">贵财标识</a></li> <li><a href="#">现任领导</a></li> <li><a href="#">发展战略</a></li> <li><a href="#">领导关怀</a></li> <li><a href="#">校友风采</a></li> <li><a href="#">大事记</a></li> <li><a href="#">历史沿革</a></li> <li><a href="#">校园风光</a></li> </ul> </div> </li> <li id="tit2"><a href="#">组织机构</a> <div id="two"> <img src="zuzhi_img.jpg"/> <ul > <li><a href="#">党群部门</a></li> <li><a href="#">行政部门</a></li> <li><a href="#">院系设置</a></li> <li><a href="#">科研机构</a></li> <li><a href="#">教辅部门</a></li> </ul> </div></li> <li id="tit3"><a href="#">招生就业</a> <div id="three"> <img src="zhaosheng_img.jpg" /> <ul > <li><a href="#">本专科招生</a></li> <li><a href="#">研究生招生</a></li> <li><a href="#">本专科就业</a></li> <li><a href="#">研究生就业</a></li> </ul> </div></li> <li id="tit4"><a href="#">教育教学</a> <div id="four"> <img src="jiaoyu_img.jpg"> <ul > <li><a href="#">师资队伍</a></li> <li><a href="#">本科生教育</a></li> <li><a href="#">研究生教育</a></li> <li><a href="#">继续教育</a></li> <li><a href="#">留学生教育(国际合作培养)</a></li> </ul> </div></li> <li id="tit5"><a href="#">科学研究</a> <div id="five"> <img src="kexue_img.jpg"/> <ul > <li><a href="#">学科建设</a></li> <li><a href="#">科研项目</a></li> <li><a href="#">科研机构</a></li> <li><a href="#">学术刊物</a></li> </ul> </div></li> <li id="tit6"><a href="#">合作交流</a> <div id="six"> <img src="hezuo_img.jpg"/> <ul > <li><a href="#">中外合作办学</a></li> <li><a href="#">孔子学院</a></li> <li><a href="#">学术交流</a></li> <li><a href="#">国际交流</a></li> </ul> </div></li> <li id="tit7"><a href="#">校园服务</a> <div id="seven"> <img src="fuwu_img.jpg"/> <ul > <li><a href="#">校园文化</a></li> <li><a href="#">校园导览</a></li> <li><a href="#">生活指南</a></li> <li><a href="#">校园媒体</a></li> <li><a href="#">道德讲堂</a></li> <li><a href="#">心理健康教育</a></li> <li><a href="#">助学服务</a></li> <li><a href="#">校历</a></li> <li><a href="#">问卷调查</a></li> </ul> </div></li> </ul> <form> <input id="search" type="text" name="search" value="请输入搜索内容..." size="20px;" /> </form><input id="search_img"type="image" src="search_button.png"/> </div> </div> <img id="bg" src="bga2.jpg" > <p>By Eastmount CSDN</p> </body> </html> 运行如下图所示,鼠标移动到不同位置可以显示不同下拉菜单,非常实用的例子。 五. JS图片放大镜功能-类淘宝 代码如下所示:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>放大镜</title> <style type="text/css"> #div1 { width: 120px; height: 90px; padding: 5px; border: 1px solid #ccc; position: relative; } #div1 .small_pic { width: 120px; height: 90px; background: #eee; position: relative; } #div1 .float_layer { width: 50px; height: 50px; border: 1px solid #000; background: #fff; filter: alpha(opacity: 30); opacity: 0.3; position: absolute; top: 0; left: 0; display:none; } #div1 .mark {width:100%; height:100%; position:absolute; z-index:2; left:0px; top:0px; background:red; filter:alpha(opacity:0); opacity:0;} #div1 .big_pic { position: absolute; top: -1px; left: 215px; width:250px; height:250px; overflow:hidden; border:2px solid #CCC; display:none; } #div1 .big_pic img { position:absolute; top: -30px; left: -80px; } </style> <script type="text/javascript"> function getByClass(oParent, sClass) { var aEle=oParent.getElementsByTagName('*'); var aTmp=[]; var i=0; for(i=0;i<aEle.length;i++) { if(aEle[i].className==sClass) { aTmp.push(aEle[i]); } } return aTmp; } window.onload=function () { var oDiv=document.getElementById('div1'); var oMark=getByClass(oDiv, 'mark')[0]; var oFloat=getByClass(oDiv, 'float_layer')[0]; var oBig=getByClass(oDiv, 'big_pic')[0]; var oSmall=getByClass(oDiv, 'small_pic')[0]; var oImg=oBig.getElementsByTagName('img')[0]; oMark.onmouseover=function () { oFloat.style.display='block'; oBig.style.display='block'; }; oMark.onmouseout=function () { oFloat.style.display='none'; oBig.style.display='none'; }; oMark.onmousemove=function (ev) { var oEvent=ev||event; var l=oEvent.clientX-oDiv.offsetLeft-oSmall.offsetLeft-oFloat.offsetWidth/2; var t=oEvent.clientY-oDiv.offsetTop-oSmall.offsetTop-oFloat.offsetHeight/2; if(l<0) { l=0; } else if(l>oMark.offsetWidth-oFloat.offsetWidth) { l=oMark.offsetWidth-oFloat.offsetWidth; } if(t<0) { t=0; } else if(t>oMark.offsetHeight-oFloat.offsetHeight) { t=oMark.offsetHeight-oFloat.offsetHeight; } oFloat.style.left=l+'px'; oFloat.style.top=t+'px'; var percentX=l/(oMark.offsetWidth-oFloat.offsetWidth); var percentY=t/(oMark.offsetHeight-oFloat.offsetHeight); oImg.style.left=-percentX*(oImg.offsetWidth-oBig.offsetWidth)+'px'; oImg.style.top=-percentY*(oImg.offsetHeight-oBig.offsetHeight)+'px'; }; }; </script> </head> <body> <div id="div1"> <div class="small_pic"> <span class="mark"></span> <span class="float_layer"></span> <img src="wall_s6.jpg" /></div> <div class="big_pic"><img src="wall6.jpg" /></div> </div> </body> </html> 运行结果如下图所示,代码较难。 六. 实现下一页翻页功能 代码如下所示,该段代码实现点击"下一页"翻页功能。 <!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> <style type="text/css"> #top { background-color: #8EC7FF; height: 45px; width: 1030px; float: left; border-top-width: thin; border-right-width: thin; border-bottom-width: thin; border-left-width: thin; border-bottom-style: solid; border-top-color: #FFF; border-right-color: #FFF; border-bottom-color: #FFF; border-left-color: #FFF; } #own { width: 1030px; margin: 0 auto; } h2 { display: inline; float: left; font-size: 18px; font-weight: 400; margin-top: -12px; } #message { height: 30px; width: 1030px; background-color: #CCCCCC; float: left; border-top-width: thin; border-right-width: thin; border-bottom-width: thin; border-left-width: thin; border-bottom-style: solid; border-top-color: #999; border-right-color: #999; border-bottom-color: #999; border-left-color: #999; } #own #message .h1 { width: 150px; } #own #message .h2 { width: 700px; } #top .sb { height: 30px; width: 100px; margin-left: 10px; margin-top: 10px; } #top .sb1 { height: 30px; width: 120px; margin-left: 10px; margin-top: 10px; } #buttom { width: 1030px; float: left; margin-top: 10px; } td{ border-bottom:#CCC solid 1px; border-collapse:collapse; } a{ text-decoration:none; color:#333; } a:link {color: #333} /* 未访问的链接 */ a:visited {color:#00C;} /* 已访问的链接 */ a:hover {color:#99C;} </style> <!--实现全选--> <script type="text/javascript" src="js/jquery-1.4.4.min.js"></script> <script type="text/javascript"> $(function(){ $('#selectAll').click(function(){ $('input[type=checkbox]').attr('checked', $(this).attr('checked')); }); }); </script> <!--批量删除--> <script language="javascript"> function deleteAll(obj){ var checked = document.getElementsByName(obj); for(var i = 0; i < checked.length; i ++){ if(checked[i].checked){ var tr=checked[i].parentNode.parentNode; var tbody=tr.parentNode; tbody.removeChild(tr); i--; } } } </script> <script type="text/javascript"> function goPage(pno,psize){ var itable = document.getElementById("idData"); var num = itable.rows.length;//表格所有行数(所有记录数) console.log(num); var totalPage = 0;//总页数 var pageSize = psize;//每页显示行数 //总共分几页 if(num/pageSize > parseInt(num/pageSize)){ totalPage=parseInt(num/pageSize)+1; }else{ totalPage=parseInt(num/pageSize); } var currentPage = pno;//当前页数 var startRow = (currentPage - 1) * pageSize+1;//开始显示的行 31 var endRow = currentPage * pageSize;//结束显示的行 40 endRow = (endRow > num)? num : endRow; 40 console.log(endRow); //遍历显示数据实现分页 for(var i=1;i<(num+1);i++){ var irow = itable.rows[i-1]; if(i>=startRow && i<=endRow){ irow.style.display = "block"; }else{ irow.style.display = "none"; } } var pageEnd = document.getElementById("pageEnd"); var tempStr = "共"+num+"条记录 分"+totalPage+"页 当前第"+currentPage+"页"; if(currentPage>1){ tempStr += "<a href=\"#\" onClick=\"goPage("+(1)+","+psize+")\">首页</a>"; tempStr += "<a href=\"#\" onClick=\"goPage("+(currentPage-1)+","+psize+")\"><上一页</a>" }else{ tempStr += "首页"; tempStr += "<上一页"; } if(currentPage<totalPage){ tempStr += "<a href=\"#\" onClick=\"goPage("+(currentPage+1)+","+psize+")\">下一页></a>"; tempStr += "<a href=\"#\" onClick=\"goPage("+(totalPage)+","+psize+")\">尾页</a>"; }else{ tempStr += "下一页>"; tempStr += "尾页"; } document.getElementById("barcon").innerHTML = tempStr; } </script> <script> function td() { document.getElementById("btn1").value="已读"; } function td1() { document.getElementById("btn2").value="已读"; } </script> </head> <body onload ="goPage(1,5);" > <div id="own"> <div id="top"> <input class="sb" type="submit" id="" value="删除" name="delete_button" onclick="deleteAll('range');" /> </div> <div id="buttom"> <span>我的消息</span> <div><hr color="#00CCFF" width="1030px"/></div> <table cellPadding=0 cellSpacing=0 style="text-align:center" id="idData"> <tbody> <tr style="height:35px;"> <td width="45px"><input type="checkbox" value="" name="range" id="selectAll"/></td> <td width="120px">反馈人</td> <td width="680px">反馈内容</td> <td width="150px">反馈时间</td> <td width="50px">操作</td> </tr> <tr style="height:32px;"> <td width="45px"><input type="checkbox" value="" name="range"/></td> <td width="120px">沈敏</td> <td width="680px"><a href="xxxiangxi.html" target="menuFrame" onclick="iframe.location='xxxiangxi.html'">你好呀!!!!!!!!!!!!!!!!!!!!!!!!!!!!</a></td> <td width="150px">2016/11/13</td> <td width="50px"><a href="xxxiangxi.html" target="menuFrame" onclick="iframe.location='xxxiangxi.html'"><input class="sb" type="submit" value="未读" id="btn1" onclick="td()"/></a></td> </tr> <tr style="height:32px;"> <td width="45px"><input type="checkbox" value="" name="range"/></td> <td width="120px">沈敏</td> <td width="680px"><a href="note.html" target="menuFrame" onclick="iframe.location='note.html'">你好呀!!!!!!!!!!!!!!!!!!!!!!!!!!!!</a></td> <td width="150px">2016/11/13</td> <td width="50px"><a href="note.html" target="_blank"><input class="sb" type="submit" id="btn2" onclick="td1()" value="未读" /></a></td> </tr> <tr style="height:32px;"> <td width="45px"><input type="checkbox" value="" name="range"/></td> <td width="120px">沈敏</td> <td width="680px"><a href="note.html" target="menuFrame" onclick="iframe.location='note.html'">你好呀!!!!!!!!!!!!!!!!!!!!!!!!!!!!</a></td> <td width="150px">2016/11/13</td> <td width="50px"><a href="note.html" target="_blank"><input class="sb" type="submit" id="btn2" onclick="td1()" value="未读" /></a></td> </tr> <tr style="height:32px;"> <td width="45px"><input type="checkbox" value="" name="range"/></td> <td width="120px">沈敏</td> <td width="680px"><a href="note.html" target="menuFrame" onclick="iframe.location='note.html'">你好呀!!!!!!!!!!!!!!!!!!!!!!!!!!!!</a></td> <td width="150px">2016/11/13</td> <td width="50px"><a href="note.html" target="_blank"><input class="sb" type="submit" id="btn2" onclick="td1()" value="未读" /></a></td> </tr> <tr style="height:32px;"> <td width="45px"><input type="checkbox" value="" name="range"/></td> <td width="120px">沈敏</td> <td width="680px"><a href="note.html" target="menuFrame" onclick="iframe.location='note.html'">你好呀!!!!!!!!!!!!!!!!!!!!!!!!!!!!</a></td> <td width="150px">2016/11/13</td> <td width="50px"><a href="note.html" target="_blank"><input class="sb" type="submit" id="btn2" onclick="td1()" value="未读" /></a></td> </tr> <tr style="height:32px;"> <td width="45px"><input type="checkbox" value="" name="range"/></td> <td width="120px">沈敏</td> <td width="680px"><a href="note.html" target="menuFrame" onclick="iframe.location='note.html'">你好呀!!!!!!!!!!!!!!!!!!!!!!!!!!!!</a></td> <td width="150px">2016/11/13</td> <td width="50px"><a href="note.html" target="_blank"><input class="sb" type="submit" id="btn2" onclick="td1()" value="未读" /></a></td> </tr> </tbody> </table> <table width="100%" align="right"> <tr><td style="padding-left:640px;"><div id="barcon" name="barcon"></div></td></tr> </table> </div> </div> </body> </html> 运行结果如下所示: 最后希望这篇文章对你有所帮助,尤其是我的学生和JS学习者。 最近非常开心,感谢娜娜,晚安~
这篇文章主要介绍三个知识点,也是我《数据挖掘与分析》课程讲课的内容。 1.关联规则挖掘概念及实现过程; 2.Apriori算法挖掘频繁项集; 3.Python实现关联规则挖掘及置信度、支持度计算。 前文推荐: 【Python数据挖掘课程】一.安装Python及爬虫入门介绍 【Python数据挖掘课程】二.Kmeans聚类数据分析及Anaconda介绍 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化 【Python数据挖掘课程】四.决策树DTC数据分析及鸢尾数据集分析 【Python数据挖掘课程】五.线性回归知识及预测糖尿病实例 【Python数据挖掘课程】六.Numpy、Pandas和Matplotlib包基础知识 【Python数据挖掘课程】七.PCA降维操作及subplot子图绘制 希望这篇文章对你有所帮助,尤其是刚刚接触数据挖掘以及大数据的同学,这些基础知识真的非常重要。如果文章中存在不足或错误的地方,还请海涵~ 参考: 关联规则挖掘之Apriori算法实现超市购物 - eastmount 关联规则简介与Apriori算法 - 百度文库guaidaoK 一. 关联规则挖掘概念及实现过程 1.关联规则 关联规则(Association Rules)是反映一个事物与其他事物之间的相互依存性和关联性,如果两个或多个事物之间存在一定的关联关系,那么,其中一个事物就能通过其他事物预测到。关联规则是数据挖掘的一个重要技术,用于从大量数据中挖掘出有价值的数据项之间的相关关系。 关联规则首先被Agrawal, lmielinski and Swami在1993年的SIGMOD会议上提出。 关联规则挖掘的最经典的例子就是沃尔玛的啤酒与尿布的故事,通过对超市购物篮数据进行分析,即顾客放入购物篮中不同商品之间的关系来分析顾客的购物习惯,发现美国妇女们经常会叮嘱丈夫下班后为孩子买尿布,30%-40%的丈夫同时会顺便购买喜爱的啤酒,超市就把尿布和啤酒放在一起销售增加销售额。有了这个发现后,超市调整了货架的设置,把尿布和啤酒摆放在一起销售,从而大大增加了销售额。 2.常见案例 前面讲述了关联规则挖掘对超市购物篮的例子,使用Apriori对数据进行频繁项集挖掘与关联规则的产生是一个非常有用的技术,其中我们众所周知的例子如: (1) 沃尔玛超市的尿布与啤酒 (2) 超市的牛奶与面包 (3) 百度文库推荐相关文档 (4) 淘宝推荐相关书籍 (5) 医疗推荐可能的治疗组合 (6) 银行推荐相关联业务 这些都是商务智能和关联规则在实际生活中的运用。 3.置信度与支持度 (1) 什么是规则? 规则形如"如果…那么…(If…Then…)",前者为条件,后者为结果。例如一个顾客,如果买了可乐,那么他也会购买果汁。 如何来度量一个规则是否够好?有两个量,置信度(Confidence)和支持度(Support),假如存在如下表的购物记录。 (2) 基本概念 关联规则挖掘是寻找给定数据集中项之间的有趣联系。如下图所示: 其中,I={ I1, I2, … Im } 是m个不同项目的集合,集合中的元素称为项目(Item)。 项目的集合I称为项目集合(Itemset),长度为k的项集成为k-项集(k-Itemset)。 设任务相关的数据D是数据库事务的集合,其中每个事务T是项的集合,使得T⊆I。每个事务有一个标识符TID;设A是一个项集,事务T包含A当且仅当A⊆I,则关联规则形式为A=>B(其中A⊂I,B⊂I,并且A∩B= ∅),交易集D中包含交易的个数记为|D|。 在关联规则度量中有两个重要的度量值:支持度和置信度。 对于关联规则R:A=>B,则: 支持度(suppport):是交易集中同时包含A和B的交易数与所有交易数之比。 Support(A=>B)=P(A∪B)=count(A∪B)/|D| 置信度(confidence):是包含A和B交易数与包含A的交易数之比。 Confidence(A=>B)=P(B|A)=support(A∪B)/support(A) (3) 支持度 支持度(Support)计算在所有的交易集中,既有A又有B的概率。例如在5条记录中,既有橙汁又有可乐的记录有2条。则此条规则的支持度为 2/5=0.4,即: Support(A=>B)=P(AB) 现在这条规则可表述为,如果一个顾客购买了橙汁,则有50%(置信度)的可能购买可乐。而这样的情况(即买了橙汁会再买可乐)会有40%(支持度)的可能发生。 (4) 置信度 置信度(confidence)表示了这条规则有多大程度上值得可信。设条件的项的集合为A,结果的集合为B。置信度计算在A中,同时也含有B的概率(即:if A ,then B的概率)。即 : Confidence(A=>B)=P(B|A) 例如计算“如果Orange则Coke”的置信度。由于在含有“橙汁”的4条交易中,仅有2条交易含有“可乐”,其置信度为0.5。 (5) 最小支持度与频繁集 发现关联规则要求项集必须满足的最小支持阈值,称为项集的最小支持度(Minimum Support),记为supmin。支持度大于或等于supmin的项集称为频繁项集,简称频繁集,反之则称为非频繁集。通常k-项集如果满足supmin,称为k-频繁集,记作Lk。关联规则的最小置信度(Minimum Confidence)记为confmin,它表示关联规则需要满足的最低可靠性。 (6) 关联规则 (7) 强关联规则 如果规则R:X=>Y 满足 support(X=>Y) >= supmin 且 confidence(X=>Y)>=confmin,称关联规则X=>Y为强关联规则,否则称关联规则X=>Y为弱关联规则。 在挖掘关联规则时,产生的关联规则要经过supmin和confmin的衡量,筛选出来的强关联规则才能用于指导商家的决策。 二. Apriori算法挖掘频繁项集 关联规则对购物篮进行挖掘,通常采用两个步骤进行: a.找出所有频繁项集(文章中我使用Apriori算法>=最小支持度的项集) b.由频繁项集产生强关联规则,这些规则必须大于或者等于最小支持度和最小置信度。 下面将通超市购物的例子对关联规则挖掘Apriori算法进行分析。 Apriori算法是一种对有影响的挖掘布尔关联规则频繁项集的算法,通过算法的连接和剪枝即可挖掘频繁项集。 Apriori算法将发现关联规则的过程分为两个步骤: 1.通过迭代,检索出事务数据库中的所有频繁项集,即支持度不低于用户设定的阈值的项集; 2.利用频繁项集构造出满足用户最小置信度的规则。 挖掘或识别出所有频繁项集是该算法的核心,占整个计算量的大部分。 补充频繁项集相关知识: K-项集:指包含K个项的项集; 项集的出现频率:指包含项集的事务数,简称为项集的频率、支持度计数或计数; 频繁项集:如果项集的出现频率大于或等于最小支持度计数阈值,则称它为频繁项集,其中频繁K-项集的集合通常记作Lk。 下面直接通过例子描述该算法:如下图所示,使用Apriori算法关联规则挖掘数据集中的频繁项集。(最小支持度计数为2) 具体过程如下所示: 具体分析结果: 第一次扫描:对每个候选商品计数得C1,由于候选{D}支持度计数为1<最小支持度计数2,故删除{D}得频繁1-项集合L1; 第二次扫描:由L1产生候选C2并对候选计数得C2,比较候选支持度计数与最小支持度计数2得频繁2-项集合L2; 第三次扫描:用Apriori算法对L2进行连接和剪枝产生候选3项集合C3的过程如下: 1.连接: C3=L2(连接)L2={{A,C},{B,C},{B,E},{C,E}}{{A,C},{B,C},{B,E},{C,E}}={{A,B,C},{A,C,E},{B,C,E}} 2.剪枝: {A,B,C}的2项子集{A,B},{A,C}和{B,C},其中{A,B}不是2项子集L2,因此不是频繁的,从C3中删除; {A,C,E}的2项子集{A,C},{A,E}和{C,E},其中{A,E}不是2项子集L2,因此不是频繁的,从C3中删除; {B,C,E}的2项子集{B,C},{B,E}和{C,E},它的所有2项子集都是L2的元素,保留C3中。 经过Apriori算法对L2连接和剪枝后产生候选3项集的集合为C3={B,C,E}. 在对该候选商品计数,由于等于最小支持度计数2,故得频繁3-项集合L3,同时由于4-项集中仅1个,故C4为空集,算法终止。 三. 举例:频繁项集产生强关联规则 强关联规:如果规则R:X=>Y满足support(X=>Y)>=supmin(最小支持度,它用于衡量规则需要满足的最低重要性)且confidence(X=>Y)>=confmin(最小置信度,它表示关联规则需要满足的最低可靠性)称关联规则X=>Y为强关联规则,否则称关联规则X=>Y为弱关联规则。 例子: 现有A、B、C、D、E五种商品的交易记录表,找出所有频繁项集,假设最小支持度>=50%,最小置信度>=50%。 对于关联规则R:A=>B,则: 支持度(suppport):是交易集中同时包含A和B的交易数与所有交易数之比。 Support(A=>B)=P(A∪B)=count(A∪B)/|D| 置信度(confidence):是包含A和B交易数与包含A的交易数之比。 Confidence(A=>B)=P(B|A)=support(A∪B)/support(A) 计算过程如下,K=1的时候项集{A}在T1、T3中出现2次,共4条交易,故支持度为2/4=50%,依次计算。其中项集{D}在T1出现,其支持度为1/4=25%,小于最小支持度50%,故去除,得到L1。 然后对L1中项集两两组合,再分别计算其支持度,其中项集{A, B}在T3中出现1次,其支持度=1/4=25%,小于最小支持度50%,故去除,同理得到L2项集。 然后如下图所示,对L2中的项集进行组合,其中超过三项的进行过滤,最后计算得到L3项集{B,C,E}。 最后对计算置信度,如下图所示。 Apriori算法弊端:需要多次扫描数据表。如果频繁集最多包含10个项,那么就需要扫描交易数据表10遍,这需要很大的I/O负载。同时,产生大量频繁集,若有100个项目,可能产生候选项数目。 故:Jiawei Han等人在2000年提出了一种基于FP-树的关联规则挖掘算法FP_growth,它采取“分而治之”的策略,将提供频繁项目集的数据库压缩成一棵频繁模式树(FP-树)。 推荐一张图,详细分析关联规则的过程: 参考文献: [1]高明 . 关联规则挖掘算法的研究及其应用[D].山东师范大学. 2006 [2]李彦伟 . 基于关联规则的数据挖掘方法研究[D].江南大学. 2011 [3]肖劲橙,林子禹,毛超.关联规则在零售商业的应用[J].计算机工程.2004,30(3):189-190. [4]秦亮曦,史忠植.关联规则研究综述[J].广西大学学报.2005,30(4):310-317. [5]陈志泊,韩慧,王建新,孙俏,聂耿青.数据仓库与数据挖掘[M].北京:清华大学出版社.2009. [6]沈良忠.关联规则中Apriori 算法的C#实现研究[J].电脑知识与技术.2009,5(13):3501-3504. [7]赵卫东.商务智能(第二版)[M].北京:清华大学出版社.2011. 四. Python实现关联规则挖掘及置信度、支持度计算 由于这部分代码在Sklearn中没有相关库,自己后面会实现并替换,目前参考空木大神的博客。地址:http://blog.csdn.net/u010454729/article/details/49078505 # -*- coding: utf-8 -*- """ Created on Mon Nov 28 03:29:51 2016 地址:http://blog.csdn.net/u010454729/article/details/49078505 @author: 参考CSDN u010454729 """ # coding=utf-8 def loadDataSet(): return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]] def createC1(dataSet): #构建所有候选项集的集合 C1 = [] for transaction in dataSet: for item in transaction: if not [item] in C1: C1.append([item]) #C1添加的是列表,对于每一项进行添加,{1},{3},{4},{2},{5} C1.sort() return map(frozenset, C1) #使用frozenset,被“冰冻”的集合,为后续建立字典key-value使用。 def scanD(D,Ck,minSupport): #由候选项集生成符合最小支持度的项集L。参数分别为数据集、候选项集列表,最小支持度 ssCnt = {} for tid in D: #对于数据集里的每一条记录 for can in Ck: #每个候选项集can if can.issubset(tid): #若是候选集can是作为记录的子集,那么其值+1,对其计数 if not ssCnt.has_key(can):#ssCnt[can] = ssCnt.get(can,0)+1一句可破,没有的时候为0,加上1,有的时候用get取出,加1 ssCnt[can] = 1 else: ssCnt[can] +=1 numItems = float(len(D)) retList = [] supportData = {} for key in ssCnt: support = ssCnt[key]/numItems #除以总的记录条数,即为其支持度 if support >= minSupport: retList.insert(0,key) #超过最小支持度的项集,将其记录下来。 supportData[key] = support return retList, supportData def aprioriGen(Lk, k): #创建符合置信度的项集Ck, retList = [] lenLk = len(Lk) for i in range(lenLk): for j in range(i+1, lenLk): #k=3时,[:k-2]即取[0],对{0,1},{0,2},{1,2}这三个项集来说,L1=0,L2=0,将其合并得{0,1,2},当L1=0,L2=1不添加, L1 = list(Lk[i])[:k-2] L2 = list(Lk[j])[:k-2] L1.sort() L2.sort() if L1==L2: retList.append(Lk[i]|Lk[j]) return retList def apriori(dataSet, minSupport = 0.5): C1 = createC1(dataSet) D = map(set,dataSet) L1, supportData = scanD(D,C1,minSupport) L = [L1] #L将包含满足最小支持度,即经过筛选的所有频繁n项集,这里添加频繁1项集 k = 2 while (len(L[k-2])>0): #k=2开始,由频繁1项集生成频繁2项集,直到下一个打的项集为空 Ck = aprioriGen(L[k-2], k) Lk, supK = scanD(D, Ck, minSupport) supportData.update(supK) #supportData为字典,存放每个项集的支持度,并以更新的方式加入新的supK L.append(Lk) k +=1 return L,supportData dataSet = loadDataSet() C1 = createC1(dataSet) print "所有候选1项集C1:\n",C1 D = map(set, dataSet) print "数据集D:\n",D L1, supportData0 = scanD(D,C1, 0.5) print "符合最小支持度的频繁1项集L1:\n",L1 L, suppData = apriori(dataSet) print "所有符合最小支持度的项集L:\n",L print "频繁2项集:\n",aprioriGen(L[0],2) L, suppData = apriori(dataSet, minSupport=0.7) print "所有符合最小支持度为0.7的项集L:\n",L 输出结果:所有候选1项集C1: [frozenset([1]), frozenset([2]), frozenset([3]), frozenset([4]), frozenset([5])] 数据集D: [set([1, 3, 4]), set([2, 3, 5]), set([1, 2, 3, 5]), set([2, 5])] 符合最小支持度的频繁1项集L1: [frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])] 所有符合最小支持度的项集L: [[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([3, 5])], [frozenset([2, 3, 5])], []] 频繁2项集: [frozenset([1, 3]), frozenset([1, 2]), frozenset([1, 5]), frozenset([2, 3]), frozenset([3, 5]), frozenset([2, 5])] 所有符合最小支持度为0.7的项集L: [[frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([2, 5])], []] 最后希望这篇文章对你有所帮助,尤其是我的学生和接触数据挖掘、机器学习的博友。星期天晚上和思华在办公室写到三点半,庆幸这么好多可爱的学生,自己也在成长,经历很多终究是好事,最近沉醉某些事中,希望能成真!加油~
这篇文章主要介绍四个知识点,也是我那节课讲课的内容。 1.PCA降维操作; 2.Python中Sklearn的PCA扩展包; 3.Matplotlib的subplot函数绘制子图; 4.通过Kmeans对糖尿病数据集进行聚类,并绘制子图。 前文推荐: 【Python数据挖掘课程】一.安装Python及爬虫入门介绍 【Python数据挖掘课程】二.Kmeans聚类数据分析及Anaconda介绍 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化 【Python数据挖掘课程】四.决策树DTC数据分析及鸢尾数据集分析 【Python数据挖掘课程】五.线性回归知识及预测糖尿病实例 【Python数据挖掘课程】六.Numpy、Pandas和Matplotlib包基础知识 希望这篇文章对你有所帮助,尤其是刚刚接触数据挖掘以及大数据的同学,这些基础知识真的非常重要。如果文章中存在不足或错误的地方,还请海涵~ 一. PCA降维 参考文章:http://blog.csdn.net/xl890727/article/details/16898315 参考书籍:《机器学习导论》 任何分类和回归方法的复杂度都依赖于输入的数量,但为了减少存储量和计算时间,我们需要考虑降低问题的维度,丢弃不相关的特征。同时,当数据可以用较少的维度表示而不丢失信息时,我们可以对数据绘图,可视化分析它的结构和离群点。 特征降维是指采用一个低纬度的特征来表示高纬度。特征降维一般有两类方法:特征选择(Feature Selection)和特征提取(Feature Extraction)。 1.特征选择是从高纬度的特征中选择其中的一个子集来作为新的特征。最佳子集是以最少的维贡献最大的正确率,丢弃不重要的维,使用合适的误差函数进行,方法包括在向前选择(Forword Selection)和在向后选择(Backward Selection)。 2.特征提取是指将高纬度的特征经过某个函数映射至低纬度作为新的特征。常用的特征抽取方法就是PCA(主成分分析)和LDA(线性判别分析) 。 下面着重介绍PCA。 降维的本质是学习一个映射函数f:X->Y,其中X是原始数据点,用n维向量表示。Y是数据点映射后的r维向量,其中n>r。通过这种映射方法,可以将高维空间中的数据点 主成分分析(Principal Component Analysis,PCA)是一种常用的线性降维数据分析方法,其实质是在能尽可能好的代表原特征的情况下,将原特征进行线性变换、映射至低纬度空间中。 PCA通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的这组变量叫主成分,它可用于提取数据的主要特征分量,常用于高维数据的降维。 该方法的重点在于:能否在各个变量之间相关关系研究基础上,用较少的新变量代替原来较多的变量,而且这些较少新变量尽可能多地保留原来较多的变量所反映的信息,又能保证新指标之间保持相互无关(信息不重叠)。 图形解释:上图将二维样本的散点图降为一维表示,理想情况是这个1维新向量包含原始数据最多的信息,选择那条红色的线,类似于数据的椭圆长轴,该方向离散程度最大,方差最大,包含的信息量最多。短轴方向上的数据变化很少,对数据的解释能力弱。 原理解释: 下面引用xl890727的一张图片简单讲解,因为我数学实在好弱,恶补中。 PCA是多变量分析中最老的技术之一,它源于通信理论中的K-L变换。 其结果是该点到n个样本之间的距离最小,从而通过该点表示这n个样本。 详细过程: 下面是主成分分析算法的过程,还是那句话:数学太差是硬伤,所以参考的百度文库的,还请海涵,自己真的得加强数学。 总结PCA步骤如下图所示: 推荐参考资料: http://blog.codinglabs.org/articles/pca-tutorial.html - by: 张洋 特征降维-PCA(Principal Component Analysis) - xl890727 PCA的原理及详细步骤 - 百度文库 二. Python中Sklearn的PCA扩展包 下面介绍Sklearn中PCA降维的方法,参考网址: http://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html 导入方法:from sklearn.decomposition import PCA 调用函数如下,其中n_components=2表示降低为2维。pca = PCA(n_components=2) 例如下面代码进行PCA降维操作:import numpy as np from sklearn.decomposition import PCA X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]]) pca = PCA(n_components=2) print pca pca.fit(X) print(pca.explained_variance_ratio_) 输出结果如下所示:PCA(copy=True, n_components=2, whiten=False) [ 0.99244291 0.00755711] 再如载入boston数据集,总共10个特征,降维成两个特征:#载入数据集 from sklearn.datasets import load_boston d = load_boston() x = d.data y = d.target print x[:10] print u'形状:', x.shape #降维 import numpy as np from sklearn.decomposition import PCA pca = PCA(n_components=2) newData = pca.fit_transform(x) print u'降维后数据:' print newData[:4] print u'形状:', newData.shape 输出结果如下所示,降低为2维数据。[[ 6.32000000e-03 1.80000000e+01 2.31000000e+00 0.00000000e+00 5.38000000e-01 6.57500000e+00 6.52000000e+01 4.09000000e+00 1.00000000e+00 2.96000000e+02 1.53000000e+01 3.96900000e+02 4.98000000e+00] [ 2.73100000e-02 0.00000000e+00 7.07000000e+00 0.00000000e+00 4.69000000e-01 6.42100000e+00 7.89000000e+01 4.96710000e+00 2.00000000e+00 2.42000000e+02 1.78000000e+01 3.96900000e+02 9.14000000e+00] [ 2.72900000e-02 0.00000000e+00 7.07000000e+00 0.00000000e+00 4.69000000e-01 7.18500000e+00 6.11000000e+01 4.96710000e+00 2.00000000e+00 2.42000000e+02 1.78000000e+01 3.92830000e+02 4.03000000e+00] [ 3.23700000e-02 0.00000000e+00 2.18000000e+00 0.00000000e+00 4.58000000e-01 6.99800000e+00 4.58000000e+01 6.06220000e+00 3.00000000e+00 2.22000000e+02 1.87000000e+01 3.94630000e+02 2.94000000e+00]] 形状: (506L, 13L) 降维后数据: [[-119.81821283 5.56072403] [-168.88993091 -10.11419701] [-169.31150637 -14.07855395] [-190.2305986 -18.29993274]] 形状: (506L, 2L) 推荐大家阅读官方的文档,里面的内容可以学习,例如Iris鸢尾花降维。 三. Kmeans聚类糖尿病及降维subplot绘制子图 绘制多子图 Matplotlib 里的常用类的包含关系为 Figure -> Axes -> (Line2D, Text, etc.)。一个Figure对象可以包含多个子图(Axes),在matplotlib中用Axes对象表示一个绘图区域,可以理解为子图。可以使用subplot()快速绘制包含多个子图的图表,它的调用形式如下: subplot(numRows, numCols, plotNum) subplot将整个绘图区域等分为numRows行* numCols列个子区域,然后按照从左到右,从上到下的顺序对每个子区域进行编号,左上的子区域的编号为1。如果numRows,numCols和plotNum这三个数都小于10的话,可以把它们缩写为一个整数,例如subplot(323)和subplot(3,2,3)是相同的。subplot在plotNum指定的区域中创建一个轴对象。如果新创建的轴和之前创建的轴重叠的话,之前的轴将被删除。 当前的图表和子图可以使用gcf()和gca()获得,它们分别是“Get Current Figure”和“Get Current Axis”的开头字母缩写。gcf()获得的是表示图表的Figure对象,而gca()则获得的是表示子图的Axes对象。下面我们在Python中运行程序,然后调用gcf()和gca()查看当前的Figure和Axes对象。 import numpy as np import matplotlib.pyplot as plt plt.figure(1) # 创建图表1 plt.figure(2) # 创建图表2 ax1 = plt.subplot(211) # 在图表2中创建子图1 ax2 = plt.subplot(212) # 在图表2中创建子图2 x = np.linspace(0, 3, 100) for i in xrange(5): plt.figure(1) # 选择图表1 plt.plot(x, np.exp(i*x/3)) plt.sca(ax1) # 选择图表2的子图1 plt.plot(x, np.sin(i*x)) plt.sca(ax2) # 选择图表2的子图2 plt.plot(x, np.cos(i*x)) plt.show() 输出如下图所示: 详细代码 下面这个例子是通过Kmeans聚类,数据集是load_diabetes载入糖尿病数据集,然后使用PCA对数据集进行降维操作,降低成两维,最后分别聚类为2类、3类、4类和5类,通过subplot显示子图。 # -*- coding: utf-8 -*- #糖尿病数据集 from sklearn.datasets import load_diabetes data = load_diabetes() x = data.data print x[:4] y = data.target print y[:4] #KMeans聚类算法 from sklearn.cluster import KMeans #训练 clf = KMeans(n_clusters=2) print clf clf.fit(x) #预测 pre = clf.predict(x) print pre[:10] #使用PCA降维操作 from sklearn.decomposition import PCA pca = PCA(n_components=2) newData = pca.fit_transform(x) print newData[:4] L1 = [n[0] for n in newData] L2 = [n[1] for n in newData] #绘图 import numpy as np import matplotlib.pyplot as plt #用来正常显示中文标签 plt.rc('font', family='SimHei', size=8) #plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示负号 plt.rcParams['axes.unicode_minus']=False p1 = plt.subplot(221) plt.title(u"Kmeans聚类 n=2") plt.scatter(L1,L2,c=pre,marker="s") plt.sca(p1) ################################### # 聚类 类蔟数=3 clf = KMeans(n_clusters=3) clf.fit(x) pre = clf.predict(x) p2 = plt.subplot(222) plt.title("Kmeans n=3") plt.scatter(L1,L2,c=pre,marker="s") plt.sca(p2) ################################### # 聚类 类蔟数=4 clf = KMeans(n_clusters=4) clf.fit(x) pre = clf.predict(x) p3 = plt.subplot(223) plt.title("Kmeans n=4") plt.scatter(L1,L2,c=pre,marker="+") plt.sca(p3) ################################### # 聚类 类蔟数=5 clf = KMeans(n_clusters=5) clf.fit(x) pre = clf.predict(x) p4 = plt.subplot(224) plt.title("Kmeans n=5") plt.scatter(L1,L2,c=pre,marker="+") plt.sca(p4) #保存图片本地 plt.savefig('power.png', dpi=300) plt.show() 输出结果如下图所示,感觉非常棒,这有利于做实验对比。 最后希望这篇文章对你有所帮助,尤其是我的学生和接触数据挖掘、机器学习的博友。本来是24号感恩节半夜写完的,实在太累,星期六来办公室写的,同时评估终于结束了,好累,但庆幸的是好多可爱的学生,自己也在成长,经历很多终究是好事,她的酒窝没有酒,我却醉得像条狗。杨老师加油~ (By:Eastmount 2016-11-26 下午4点半 )
前面几篇文章采用的案例的方法进行介绍的,这篇文章主要介绍Python常用的扩展包,同时结合数据挖掘相关知识介绍该包具体的用法,主要介绍Numpy、Pandas和Matplotlib三个包。目录: 一.Python常用扩展包 二.Numpy科学计算包 三.Pandas数据分析包 四.Matplotlib绘图包 前文推荐: 【Python数据挖掘课程】一.安装Python及爬虫入门介绍 【Python数据挖掘课程】二.Kmeans聚类数据分析及Anaconda介绍 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化 【Python数据挖掘课程】四.决策树DTC数据分析及鸢尾数据集分析 【Python数据挖掘课程】五.线性回归知识及预测糖尿病实例 希望这篇文章对你有所帮助,尤其是刚刚接触数据挖掘以及大数据的同学,这些基础知识真的非常重要。如果文章中存在不足或错误的地方,还请海涵~ 部分截图参考张良均的《Python数据分析与挖掘实战》,推荐大家购买阅读。 一. Python常用扩展包 参考张良均的《Python数据分析与挖掘实战》,下图展示了常见的Python扩展包。 常用的包主要包括: 1.Numpy Python没有提供数组,列表(List)可以完成数组,但不是真正的数据,当数据量增大时,,它的速度很慢。所以Numpy扩展包提供了数组支持,同时很多高级扩展包依赖它。例如:Scipy、Matplotlib、Pandas。 2.Scipy 该包提供矩阵支持,以及矩阵相关的数值计算模块。如果说Numpy让Python有了Matlab的味道,那么Scipy就让Python真正地成为二半个Matlib。因为涉及到矩阵内容,而课程中主要使用数组,所以不再介绍。 3.Pandas Pandas是面板数据(Panel Data)的简写。它是Python最强大的数据分析和探索工具,因金融数据分析工具而开发,支持类似SQL的数据增删改查,支持时间序列分析,灵活处理缺失数据,后面详细介绍。 4.Scikit-Learn Scikit-Learn是一个基于python的用于数据挖掘和数据分析的简单且有效的工具,它的基本功能主要被分为六个部分:分类(Classification)、回归(Regression)、聚类(Clustering)、数据降维(Dimensionality Reduction)、模型选择(Model Selection)、数据预处理(Preprocessing),前面写的很多文章算法都是出自该扩展包。 详见官网:http://scikit-learn.org/stable/ 5.Matplotlib 该包主要用于绘图和绘表,强大的数据可视化工具,做图库,语法类似MATLAB。同时,Seaborn也是数据可视化的工具包。 注意:这些包在Anaconda集成环境中已经存在,可以直接使用,最早我是通过Python2.7来编写代码的,安装过程通过pip install numpy,而且安装顺序非常讲究,容易出错,所以推荐大家使用该集成包。 二. Numpy科学计算包 NumPy(Numeric Python)系统是Python的一种开源的数值计算扩展,一个用python实现的科学计算包。它提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库。专为进行严格的数字处理而产生。 推荐学习:http://old.sebug.net/paper/books/scipydoc/numpy_intro.html 下面通过这段代码详细讲解这个包在数据分析中的常见用法: 1.一维数组处理 #导入包并重命名 import numpy as np #定义一维数组 a = np.array([2, 0, 1, 5, 8, 3]) print u'原始数据:', a #输出最大、最小值及形状 print u'最小值:', a.min() print u'最大值:', a.max() print u'形状', a.shape #数据切片 print u'切片操作:' print a[:-2] print a[-2:] print a[:1] #排序 print type(a) a.sort() print u'排序后:', a 输出结果如下所示:原始数据: [2 0 1 5 8 3] 最小值: 0 最大值: 8 形状 (6L,) 切片操作: [2 0 1 5] [8 3] [2] <type 'numpy.ndarray'> 排序后: [0 1 2 3 5 8] 核心代码: 代码通过np.array定义了一个数组[2, 0, 1, 5, 8, 3],其中min计算最小值,max计算最大值,shape表示数组的形状,因为是一维数组,故6L(6个数字)。 最重要的一个知识点是数组的切片操作,因为在数据分析过程中,通常会对数据集进行"80%-20%"或"70%-30%"的训练集和测试集划分,通常采用的方法就是切片。 a[:-2]表示从头开始获取,"-2"表示后面两个值不取,结果:[2 0 1 5] a[-2:]表示后往前数两个数字,获取数字至结尾,即获取最后两个值[8 3] a[:1]表示从头开始获取,获取1个数字,即[2] 2.二维数组处理 注意的是定义二维数组括号不要弄错,正确的应该是:[[1,2,3],[4,5,6]] 同时计算机的存储下标都是从0开始计算的。 代码如下:#定义二维数组 import numpy as np c = np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]]) #获取值 print u'形状:', c.shape print u'获取值:', c[1][0] print u'获取某行:' print c[1][:] print u'获取某行并切片:' print c[0][:-1] print c[0][-1:] #获取具体某列值 print u'获取第3列:' print c[:,np.newaxis, 2] #调用sin函数 print np.sin(np.pi/6) print type(np.sin(0.5)) #范围定义 print np.arange(0,4) print type(np.arange(0,4)) 代码输出结果如下所示: 形状: (3L, 4L) 获取值: 4 获取某行: [4 5 6 7] 获取某行并切片: [1 2 3] [4] 获取第3列: [[3] [6] [9]] 0.5 <type 'numpy.float64'> [0 1 2 3] <type 'numpy.ndarray'> 需要注意: (1)获取二维数组中的某行,如第2行数据[4,5,6,7],采用方法是:c[1][:]; (2)获取二维数组中的某列,如第2列数据[[3] [6] [9]],c[:,np.newaxis, 2]。因为通常在数据可视化中采用获取某列数据作为x或y坐标,同时多维数据也可以采用PCA降低成两维数据,再进行显示。 最后希望读者自己去阅读该段代码。 三. Pandas数据分析包 Pandas是面板数据(Panel Data)的简写。它是Python最强大的数据分析和探索工具,因金融数据分析工具而开发,支持类似SQL的数据增删改查,支持时间序列分析,灵活处理缺失数据。 注意:首先声明改包功能非常强大,我只是学习了它的非常小的一部分,后面随着学习深入会写更多它的用法,同时建议读者自行学习,不喜勿喷。 约定俗成的导入惯例: from pandas import Series, DataFrame import pandas as pd 1.常见用法:读写文件 这里读文件最常用的是两种方法: #写入excel文件: df.to_excel('foo.xlsx', sheet_name='Sheet1') #从excel文件中读取: pd.read_excel('foo.xlsx', 'Sheet1', index_col=None, na_values=['NA']) #写入csv文件: df.to_csv('foo.csv') #从csv文件中读取: pd.read_csv('foo.csv') #写入HDF5存储: df.to_hdf('foo.h5','df') #从HDF5存储中读取: pd.read_hdf('foo.h5','df') 下面通过一个具体的案例来讲解该包,这里读取的数据是张良均的《Python数据分析与挖掘实战》的第六章的电力用户数据集,missing_data.xls文件。内容如下,共3列数据,分别是用户A、用户B、用户C,共21行,对应21天的用电量,其中包含缺失值。235.8333 324.0343 478.3231 236.2708 325.6379 515.4564 238.0521 328.0897 517.0909 235.9063 514.89 236.7604 268.8324 404.048 486.0912 237.4167 391.2652 516.233 238.6563 380.8241 237.6042 388.023 435.3508 238.0313 206.4349 487.675 235.0729 235.5313 400.0787 660.2347 411.2069 621.2346 234.4688 395.2343 611.3408 235.5 344.8221 643.0863 235.6354 385.6432 642.3482 234.5521 401.6234 236 409.6489 602.9347 235.2396 416.8795 589.3457 235.4896 556.3452 236.9688 538.347 部分Excel文件数据截图如下所示: 具体代码如下所示:#读取数据 header设置Excel无标题头 import pandas as pd data = pd.read_excel("missing_data.xls", header=None) print data #计算数据长度 print u'行数', len(data) #计算用户A\B\C用电总和 print data.sum() #计算用户A\B\C用点量算术平均数 mm = data.sum() print mm #输出预览前5行数据 print u'预览前5行数据' print data.head() #输出数据基本统计量 print u'输出数据基本统计量' print data.describe() 输出结果如下所示: 0 1 2 0 235.8333 324.0343 478.3231 1 236.2708 325.6379 515.4564 2 238.0521 328.0897 517.0909 3 235.9063 NaN 514.8900 4 236.7604 268.8324 NaN 5 NaN 404.0480 486.0912 6 237.4167 391.2652 516.2330 7 238.6563 380.8241 NaN 8 237.6042 388.0230 435.3508 ... 行数 21 0 4488.9899 1 6182.3265 2 9416.3276 dtype: float64 0 4488.9899 1 6182.3265 2 9416.3276 dtype: float64 预览前5行数据 0 1 2 0 235.8333 324.0343 478.3231 1 236.2708 325.6379 515.4564 2 238.0521 328.0897 517.0909 3 235.9063 NaN 514.8900 4 236.7604 268.8324 NaN 输出数据基本统计量 0 1 2 count 19.000000 17.000000 17.000000 mean 236.262626 363.666265 553.901624 std 1.225465 57.600529 67.707729 min 234.468800 206.434900 435.350800 25% NaN NaN NaN 50% NaN NaN NaN 75% NaN NaN NaN max 238.656300 416.879500 660.234700 其中data.describe()输出数据的基本信息统计,其方法参考前面的图,包括count计数、std、max等函数。同时因为Excel表格中存在空值,故Python显示为NaN(Not a Number)表示空。 2.Series Series是一维标记数组,可以存储任意数据类型,如整型、字符串、浮点型和Python对象等,轴标一般指索引。 Series、Numpy中的一维array 、Python基本数据结构List区别:List中的元素可以是不同的数据类型,而Array和Series中则只允许存储相同的数据类型,这样可以更有效的使用内存,提高运算效率。 from pandas import Series, DataFrame #通过传递一个list对象来创建Series,默认创建整型索引; a = Series([4, 7, -5, 3]) print u'创建Series:' print a #创建一个带有索引来确定每一个数据点的Series ; b = Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c']) print u'创建带有索引的Series:' print b #如果你有一些数据在一个Python字典中,你可以通过传递字典来创建一个Series; sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000} c = Series(sdata) print u'通过传递字典创建Series:' print c states = ['California', 'Ohio', 'Oregon', 'Texas'] d = Series(sdata, index=states) print u'California没有字典为空:' print d 输出如下所示:创建Series: 0 4 1 7 2 -5 3 3 dtype: int64 创建带有索引的Series: d 4 b 7 a -5 c 3 dtype: int64 通过传递字典创建Series: Ohio 35000 Oregon 16000 Texas 71000 Utah 5000 dtype: int64 California没有字典为空: California NaN Ohio 35000.0 Oregon 16000.0 Texas 71000.0 dtype: float64 Series的一个重要功能是在算术运算中它会自动对齐不同索引的数据。 3.DataFrame DataFrame是二维标记数据结构,列可以是不同的数据类型。它是最常用的pandas对象,像Series一样可以接收多种输入:lists、dicts、series和DataFrame等。初始化对象时,除了数据还可以传index和columns这两个参数。 注意: (1) 在pandas中用函数 isnull 和 notnull 来检测数据丢失:pd.isnull(a)、pd.notnull(b)。 Series也提供了这些函数的实例方法:a.isnull()。 (2) Pandas提供了大量的方法能够轻松的对Series,DataFrame和Panel对象进行各种符合各种逻辑关系的合并操作。如:Concat、Merge (类似于SQL类型的合并)、Append (将一行连接到一个DataFrame上)。 (3) DataFrame中常常会出现重复行,DataFrame的duplicated方法返回一个布尔型Series,表示各行是否是重复行;还有一个drop_duplicated方法,它返回一个移除了重复行的DataFrame。 总之,Pandas是非常强大的一个数据分析包,很多功能都需要我自己去慢慢摸索。 四. Matplotlib画图包 Matplotlib是一个Python的图形框架,类似于MATLAB和R语言。它是python最著名的绘图库,它提供了一整套和matlab相似的命令API,十分适合交互式地进行制图。而且也可以方便地将它作为绘图控件,嵌入GUI应用程序中。 补充两张图,原自《Python数据分析与挖掘实战》,对大家绘图很有帮助。 最常用的画图函数是plot,同时常用的设置样式方法见下图。 这里主要使用前面第三部分Pandas读取的电力数据绘制图形,主要是柱状图和饼图。 1.绘制柱状图 # -*- coding: utf-8 -*- """ Created on Mon Nov 14 04:06:01 2016 @author: yxz15 """ #导入数据集 import pandas as pd data = pd.read_excel("missing_data.xls", header=None) mm = data.sum() print u'计算用电量总数:' print mm #绘制图形 import numpy as np import matplotlib.pyplot as plt #中文字体显示 plt.rc('font', family='SimHei', size=13) N = 3 #3个用户 0 1 2 ind = np.arange(N) # the x locations for the groups print ind #设置宽度 width = 0.35 x = [u'用户A', u'用户B', u'用户C'] #绘图 plt.bar(ind, mm, width, color='r', label='sum num') plt.xlabel(u"用户名") plt.ylabel(u"总耗电量") plt.title(u'电力窃漏电用户自动识别--总耗电量') plt.legend() #设置底部名称 plt.xticks(ind+width/2, x, rotation=40) #旋转40度 plt.show() 输出如下所示: 2.绘制饼图 import matplotlib.pyplot as plt fracs = [45, 30, 25] #每一块占得比例,总和为100 n = mm[0]+mm[1]+mm[2] a = (mm[0]*1.0*100/n) b = (mm[1]*1.0*100/n) c = (mm[2]*1.0*100/n) print a, b, c, n fracs = [a, b, c] explode=(0, 0, 0.08) #离开整体的距离,看效果 labels = 'A', 'B', 'C' #对应每一块的标志 plt.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90, colors = ("g", "r", "y")) # startangle是开始的角度,默认为0,从这里开始按逆时针方向依次展开 plt.title('Raining Hogs and Dogs') #标题 plt.show() 输出如下所示: 3.柱状图及比例显示import matplotlib.pyplot as plt import numpy as np plt.rc('font', family='SimHei', size=13) num = np.array([13325, 9403, 9227, 8651]) ratio = np.array([0.75, 0.76, 0.72, 0.75]) men = num * ratio women = num * (1-ratio) x = [u'聊天',u'支付',u'团购\n优惠券',u'在线视频'] width = 0.5 idx = np.arange(len(x)) plt.bar(idx, men, width, color='red', label=u'男性用户') plt.bar(idx, women, width, bottom=men, color='yellow', label=u'女性用户') plt.xlabel(u'应用类别') plt.ylabel(u'男女分布') plt.xticks(idx+width/2, x, rotation=40) plt.legend() plt.show() 输出如下所示(PS:该部分参考百度知道,网址忘记了,望提醒)。 当然该包可以绘制更多的图形,希望读者自己去学习。比如线性回归: 代码部分详解,引用前面自己写的第三篇文章: matplotlib.pyplot是用来画图的方法,matplotlib是可视化包。 [python] view plain copy plt.scatter(x, y, c=y_pred, marker='o') 绘制散点图(scatter),横轴为x,获取的第1列数据;纵轴为y,获取的第2列数据;c=y_pred对聚类的预测结果画出散点图,marker='o'说明用点表示图形。 [python] view plain copy plt.title("Kmeans-Basketball Data") 表示图形的标题为Kmeans-heightweight Data。 [python] view plain copy plt.xlabel("assists_per_minute") 表示图形x轴的标题。 [python] view plain copy plt.ylabel("points_per_minute") 表示图形y轴的标题。 [python] view plain copy plt.legend(["Rank"]) 设置右上角图例。 [python] view plain copy plt.show() 表示显示图形。 最后希望文章对你有所帮助,上课内容还需要继续探索,但enjoy myself~ 同时周末监考两天回来,确实挺累的,事情堆了很多,浪费15个小时,发现这份工作,赚点外块真不容易啊!甚至比程序猿累多了。 当老师难,当好老师更难,当贵州的好老师难上难。希望还能坚持自己的梦想,做个财大信院的扫地僧吧,但每每和学生一起还是非常享受的。同时,这次熬夜写文到深夜4点半,旁边也坐着一个自己的学生,在调试Struts、Json代码,所以说,还真不是这边的学生差,你懂得,但也并不是没有好老师,只是相对较少。fighting~ 最后补充学生冯Y的一首朋友圈感言: 把握现在,活在当下。 不以物喜,不以己悲。 闲看花开花落, 静观云卷云舒。 顺其自然,随遇而安。 我也希望自己有朝一日能达到这种心境~ 对这份工作、事业、校园、办公还是得看淡点。 (By:Eastmount 2016-11-14 中午4点半 )
这篇文章主要介绍如何使用DIV和CSS简单布局一个网站的首页,通常将网站划分为顶部(Logo、导航条)、中部(页面主要内容、左右栏目)、底部(制作方介绍、超链接)。这是非常基础的一篇引入性文章,采用案例的方式进行介绍的,希望对你有所帮助。运行结果如下图所示: main.html主页面代码 主要通过div进行布局的,同时<h2><a></a></h2>用户设置导航条,鼠标悬停时背景颜色切换。 <html> <head> <title> 网站主页 </title> <link rel="stylesheet" href="css/main.css" > </head> <!-- 首页 --> <body> <div id="bg"> <img src="images/bg.jpg" width="100%" height="100%" /> </div> <div id="bt"> <h2 class="xz"><a href="#">学校概况</a></h2> <h2><a href="jsjj.html">教师简介</a></h2> <h2><a href="#">校园生活</a></h2> <h2><a href="#">校园文化</a></h2> <h2><a href="#">联系我们</a></h2> </div> <div id="main"> <div id="left"> </div> <div id="right"> </div> </div> <div id="footer"> </div> </body> </html> css/main.css代码: 该部分代码主要是CSS文件,用于布局和设置主页面。 #bg { text-align: center; margin:0 auto; width: 80%; height: 100px; /* background-image: url("../images/bg.jpg"); */ } #bt { margin:0 auto; /* 水平居中 */ margin-top: 10px; width: 80%; height: 40px; background-color: yellow; } h2 { float: left; /* 水平显示,否则竖直显示 */ margin-top: 0px; /* 置顶 */ margin-left: 10px; width: 18%; background-color: red; height: 34px; font-size: 24px; text-align: center; padding-top: 6px; } a { text-decoration: none; } a:hover { color: black; } h2:hover { background-color: #0F0; } #main { margin:0 auto; /* 水平居中 */ margin-top: 10px; width: 80%; height: 60%; background-color: #E6E6FA; } #left { float: left; margin-left: 20px; margin-top: 5px; width: 40%; height: 90%; background-color: #9ACD32; } #right { float: left; margin-left: 20px; margin-top: 5px; width: 50%; height: 90%; background-color: #BFEFFF; } #footer { margin:0 auto; /* 水平居中 */ margin-top: 10px; width: 80%; height: 50px; background-color: #8B2500; } #main2 { margin:0 auto; /* 水平居中 */ margin-top: 10px; width: 80%; height: 60%; background-color: #F00; } .xz { float: left; /* 水平显示,否则竖直显示 */ margin-top: 0px; /* 置顶 */ margin-left: 10px; width: 18%; background-color: #0F0; height: 34px; font-size: 24px; text-align: center; padding-top: 6px; } 下面讲解几个重点: 1、在<div class="bg">布局过程中,需要在CSS中设置"margin:0 auto;",才能让它居中显示,这段代码的含义是:第一个值就是元素的上下边距0,第二个值就是左右边距。当元素的定义了width属性时,auto还能实现居中的效果。 2、在CSS中设置h2,需要添加"float: left;",使其水平显示,不增加该句的效果如下所示: 3、整个DIV布局代码如下所示,h2会向下移动一段距离,这时CSS中通过"margin-top: 4px;"进行微调。 4、悬停的核心代码如下所示,其中"text-decoration: none;"设置超链接无下划线,然后是悬停在超链接a和字体h2的变换效果。注意冒号(:)和hover之间不能有空格,否则效果消失。 简单补充CSS内容,更多的是希望你从课本中学习,这篇文章我以案例为主。 什么是CSS CSS(Cascading Style Sheet,层叠样式表)是一种格式化网页的标准方式,它扩展了 HTML 的功能,使网页设计者能够以更有效的方式设置网页格式。它是将样式信息与网页内容分离的一种标记性语言。 样式定义的语法 样式表项的组成: Selector{property1:value1;property2:value2;property3:value3;……} Selector定义样式作用的对象,property为CSS属性,value为属性对应的值。 CSS直接在标记符中嵌套 HTML 标记符的 style 属性。 例如:<P style=“text-align:center” >,其中,style属性的取值形式为“CSS属性:CSS属性值”,使用多个属性用分号分隔。 在STYLE 标记符定义样式 <STYLE>样式定义 </STYLE> 样式定义的方式为: Selector{property1:value1;property2:value2;property3:value3;……} <HTML> <HEAD><TITLE>在标记符中直接嵌套样式信息</TITLE> <STYLE> P{ font-size:24px; text-align:center } H1{ font-family:楷体_gb2312; text-align:center } </STYLE> </HEAD> <BODY> <H1>一代人</H1> <P>黑夜给了我黑色的眼睛<BR>我却用它寻找光明</P> </BODY> </HTML> 运行结果如下所示: 链接外部样式表文件 使用LINK 标记符: <LINK rel=“stylesheet” type=“text/css” href=“stylesheet.css”> 样式表文件可以用文本编辑器编辑,也可以用FP或DW编辑,内容为样式定义。 <HTML> <HEAD><TITLE>链接式样式示例</TITLE> <LINK rel="stylesheet" type="text/css" href="mycss.css"> </HEAD> <BODY> <H1>一代人</H1> <P>黑夜给了我黑色的眼睛<BR>我却用它寻找光明</P> </BODY> </HTML> 其中CSS的代码如下所示: P{ font-size:12px; text-align:center } H1{ font-family:黑体; text-align:center;background-color:green } 运行结果如下所示: 希望文章对你有所帮助,上课内容还需要继续探索。 (By:Eastmount 2016-11-08 中午12点 )
这篇文章主要讲述Echarts设置字体和线条的颜色相关操作笔记,希望文章对你有所帮助,主要是自己的在线笔记吧。我在前面先放各种修改前后图片颜色的对照,后面再详细介绍代码。这样更方便阅读及读者知道,是否对自己有所帮助,其重点是如何在模板动态网页或JSP网站中插入Echarts图片。 1.修改标题及背景颜色 2.设置柱形图颜色 3.修改坐标轴字体颜色 4.设置Legend颜色 5.修改折线颜色 6.修改油表盘字体大小及颜色 7.柱状图文本鼠标浮动上的颜色设置 推荐文章: http://echarts.baidu.com/echarts2/doc/example/bar14.html http://echarts.baidu.com/echarts2/doc/example/bar15.html 官方文档: http://echarts.baidu.com/echarts2/doc/example.html http://echarts.baidu.com/demo.html#gauge-car ECharts系列 - 柱状图(条形图)实例一 JSP 1.修改标题的颜色及背景 Echarts绘制柱状图及修改标题颜色的代码如下所示: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <script src="echarts.min.js"></script> </head> <body> <div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript"> var myChart = echarts.init(document.getElementById('main')); var labelRight = { normal: { position: 'right' } }; var labelRight = { normal: { position: 'right' } }; var option = { title: { text: '十大高耗水行业用水量八减两增 ', //标题 backgroundColor: '#ff0000', //背景 subtext: '同比百分比(%)', //子标题 textStyle: { fontWeight: 'normal', //标题颜色 color: '#408829' }, x:"center" }, legend: { data:['蒸发量','降水量','最低气温','最高气温'] }, tooltip : { trigger: 'axis', axisPointer : { // 坐标轴指示器,坐标轴触发有效 type : 'shadow' // 默认为直线,可选为:'line' | 'shadow' } }, grid: { top: 80, bottom: 80 }, xAxis: { //设置x轴 type : 'value', position: 'top', splitLine: {lineStyle:{type:'dashed'}}, max:'4', }, yAxis: { type : 'category', axisLine: {show: false}, axisLabel: {show: false}, axisTick: {show: false}, splitLine: {show: false}, data : ['石油加工、炼焦和核燃料加工业' , '非金属矿物制品业', '化学原料和化学制品制造业', '有色金属冶炼和压延加工业', '造纸和纸制品业', '纺织业', '电力、热力生产和供应业', '非金属矿采选业', '黑色金属冶炼和压延加工业', '煤炭开采和洗选业' ] }, series : [ { name: '幅度 ', type: 'bar', stack: '总量', label: { normal: { show: true, formatter: '{b}' } }, data:[ {value: 0.2, label: labelRight,itemStyle:{ normal:{color:'gray'} } }, {value: 0.7, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -1.1, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -1.3, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -1.9, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -2.9, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -3.0, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -4.2, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -4.9, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, {value: -5.8, label: labelRight,itemStyle:{ normal:{color:'gray'} }}, ] } ] }; myChart.setOption(option); window.addEventListener("resize",function() { myChart.resize(); }); </script> <div id="main2" style="width: 600px;height:400px;"> </div> </body> </html> 其中设置颜色标题的核心代码: title: { text: '十大高耗水行业用水量八减两增 ', //标题 backgroundColor: '#ff0000', //背景 subtext: '同比百分比(%)', //子标题 textStyle: { fontWeight: 'normal', //标题颜色 color: '#408829' }, x:"center" }, 输出如下图所示: 2.设置柱形图颜色 设置柱形图颜色代码如下所示,其中颜色表参考:RGB颜色查询对照表 series : [ { name: '幅度 ', type: 'bar', stack: '总量', label: { normal: { show: true, formatter: '{b}' } }, data:[ {value: 0.2, label: labelRight,itemStyle:{ normal:{color:'bule'} } }, {value: 0.7, label: labelRight,itemStyle:{ normal:{color:'green'} }}, {value: -1.1, label: labelRight,itemStyle:{ normal:{color:'red'} }}, {value: -1.3, label: labelRight,itemStyle:{ normal:{color:'#FFB6C1'} }}, {value: -1.9, label: labelRight,itemStyle:{ normal:{color:'#EE7AE9y'} }}, {value: -2.9, label: labelRight,itemStyle:{ normal:{color:'#C1FFC1'} }}, {value: -3.0, label: labelRight,itemStyle:{ normal:{color:'#AB82FF'} }}, {value: -4.2, label: labelRight,itemStyle:{ normal:{color:'#836FFF'} }}, {value: -4.9, label: labelRight,itemStyle:{ normal:{color:'#00FA9A'} }}, {value: -5.8, label: labelRight,itemStyle:{ normal:{color:'#CD00CD'} }}, ] } 输出如下图所示: 3.修改坐标字体颜色 完整代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <!-- 引入 echarts.js --> <script src="echarts.min.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div align="left" id="main" style="width: 900px;height:500px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); option = { title: { text: '2016年上半年全国规模以上工业企业用水情况(单位:亿立方米)', subtext: '数据来源:国家统计局', x: 'center', }, tooltip : { trigger: 'axis', axisPointer : { // 坐标轴指示器,坐标轴触发有效 type : 'shadow' // 默认为直线,可选为:'line' | 'shadow' } }, legend: { orient: 'vertical', x: 'left', y:"top", padding:50, data: ['用水量', '减少量',] }, grid: { left: '10', right: '60', bottom: '3%', height: '30%', top: '20%', containLabel: true }, xAxis: { type: 'value', //设置坐标轴字体颜色和宽度 axisLine:{ lineStyle:{ color:'yellow', width:2 } }, }, yAxis: { type: 'category', //设置坐标轴字体颜色和宽度 axisLine:{ lineStyle:{ color:'yellow', width:2 } }, data: ['东部地区','中部地区','西部地区',] }, series: [ { name: '用水量', type: 'bar', stack: '总量', label: { normal: { show: true, position: 'insidelift' } }, data: [109.2, 48.2, 41 ] }, { name: '减少量', type: 'bar', stack: '总量', label: { normal: { show: true, position: 'insidelift' } }, data: [1.638, 1.0122, 1.025] }, ] }; myChart.setOption(option); </script> </body> </html> 核心代码如下所示: yAxis: { type: 'category', //设置坐标轴字体颜色和宽度 axisLine:{ lineStyle:{ color:'yellow', width:2 } }, data: ['东部地区','中部地区','西部地区',] }, 输出如下图所示: 4.设置了legend颜色 核心代码代码如下: legend: { orient: 'vertical', //data:['用水量','减少量'], data:[ {name: '用水量', textStyle:{color:"#25c36c"} }, {name:'减少量', textStyle:{color:"#25c36c"}} ], x: 'left', y:"top", padding:50, }, 输出如下图所示: 5.修改折现颜色 代码如下所示: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <!-- 引入 echarts.js --> <script src="echarts.min.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); var timeData = [ '海水','陆地苦咸水','矿井水', '雨水','再生水','海水淡化水']; timeData = timeData.map(function (str) { return str.replace('2016/', ''); }); option = { title: { text: '2016年上半年全国工业用水增长率', x: 'center' }, tooltip: { trigger: 'axis', axisPointer: { animation: false } }, legend: { data:['常规用水量','非常规用水量'], x:"right", y:"top", padding: 50 }, grid: [{ left: 100, right: 100, height: '20%', top: '25%' }, { left: 100, right: 100, top: '65%', height: '25%' }], xAxis : [ { type : 'category', boundaryGap : false, axisLine: {onZero: true}, data:['地表淡水','地下淡水','自来水','其他水'] }, { gridIndex: 1, type : 'category', boundaryGap : false, axisLine: {onZero: true}, data: timeData, position: 'top' } ], yAxis : [ { name : '常规用水量(%)', type : 'value', max : 5 }, { gridIndex: 1, name : '非常规用水量(%)', type : 'value', inverse: true } ], series : [ { name:'常规用水量', type:'line', symbolSize: 8, //设置折线图颜色 itemStyle : { normal : { lineStyle:{ color:'#ff0000' } } }, hoverAnimation: false, data:[-3.8,-9.0,0.0,4.5 ] }, { name:'非常规用水量', type:'line', xAxisIndex: 1, yAxisIndex: 1, symbolSize: 8, //设置折线图颜色 itemStyle : { normal : { lineStyle:{ color:'#0000ff' } } }, hoverAnimation: false, data: [-5.8,-2.5,6.2,50.3,3.5,-3.3 ] } ] }; myChart.setOption(option); </script> </body> </html> 其中修改折现颜色代码如下所示: series : [ { name:'常规用水量', type:'line', symbolSize: 8, itemStyle : { normal : { lineStyle:{ color:'#ff0000' } } }, hoverAnimation: false, data:[-3.8,-9.0,0.0,4.5 ] }, { name:'非常规用水量', type:'line', xAxisIndex: 1, yAxisIndex: 1, itemStyle : { normal : { lineStyle:{ color:'#ff0000' } } }, symbolSize: 8, hoverAnimation: false, data: [-5.8,-2.5,6.2,50.3,3.5,-3.3 ] } ] 修改图如下所示: 6.修改油表盘字体大小及颜色 核心代码如下所示: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <!-- 引入 echarts.js --> <script src="echarts.min.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="width: 800px;height:600px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); option = { tooltip : { formatter: "{a} <br/>{c}{b}" }, toolbox: { show: true, feature: { restore: {show: true}, saveAsImage: {show: true} } }, series : [ { name: '东部地区', type: 'gauge', z: 3, min: 0, max: 120, splitNumber: 12, radius: '50%', axisLine: { // 坐标轴线 lineStyle: { // 属性lineStyle控制线条样式 width: 10 } }, axisTick: { // 坐标轴小标记 length: 15, // 属性length控制线长 lineStyle: { // 属性lineStyle控制线条样式 color: 'auto' } }, splitLine: { // 分隔线 length: 20, // 属性length控制线长 lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式 color: 'auto' } }, title : { textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE fontWeight: 'bolder', fontSize: 20, fontStyle: 'italic', color:"#25c36c" } }, detail : { textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE fontWeight: 'bolder' } }, data:[{value: 109.2, textStyle:{color:"#25c36c"}, name: ' 东部地区\n 用水量'}] }, { name: '下降', type: 'gauge', center : ['50%', '65%'], // 默认全局居中 radius : '25%', min: 0, max: 2, startAngle: 315, endAngle: 225, splitNumber: 2, axisLine: { // 坐标轴线 lineStyle: { // 属性lineStyle控制线条样式 width: 8 } }, axisTick: { // 坐标轴小标记 show: false }, axisLabel: { formatter:function(v){ switch (v + '') { case '0' : return '0'; case '1' : return '下降'; case '2' : return '1.5%'; } } }, splitLine: { // 分隔线 length: 15, // 属性length控制线长 lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式 color: 'auto' } }, pointer: { width:2 }, title: { show: false }, detail: { show: false }, data:[{value: 2, name: '下降'}] }, { name: '中部地区', type: 'gauge', center: ['20%', '55%'], // 默认全局居中 radius: '35%', min:0, max:72, endAngle:45, splitNumber:8, axisLine: { // 坐标轴线 lineStyle: { // 属性lineStyle控制线条样式 width: 8 } }, axisTick: { // 坐标轴小标记 length:12, // 属性length控制线长 lineStyle: { // 属性lineStyle控制线条样式 color: 'auto' } }, splitLine: { // 分隔线 length:20, // 属性length控制线长 lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式 color: 'auto' } }, pointer: { width:5 }, title: { offsetCenter: [0, '-30%'], // x, y,单位px }, detail: { textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE fontWeight: 'bolder' } }, data:[{value: 48.2, name: ' 中部地区ddd',textStyle:{color:"#ffff00"} }] }, { name: '下降', type: 'gauge', center : ['20%', '62%'], // 默认全局居中 radius : '25%', min: 0, max: 2, startAngle: 315, endAngle: 225, splitNumber: 2, axisLine: { // 坐标轴线 lineStyle: { // 属性lineStyle控制线条样式 width: 8 } }, axisTick: { // 坐标轴小标记 show: false }, axisLabel: { formatter:function(v){ switch (v + '') { case '0' : return '0'; case '1' : return '下降'; case '2' : return '2.1%'; } } }, splitLine: { // 分隔线 length: 15, // 属性length控制线长 lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式 color: 'auto' } }, pointer: { width:2 }, title: { show: false }, detail: { show: false }, data:[{value: 2, name: '下降'}] }, { name: '西部地区', type: 'gauge', center: ['85%', '55%'], // 默认全局居中 radius: '35%', min:0, max:72, endAngle:45, splitNumber:8, axisLine: { // 坐标轴线 lineStyle: { // 属性lineStyle控制线条样式 width: 8 } }, axisTick: { // 坐标轴小标记 length:12, // 属性length控制线长 lineStyle: { // 属性lineStyle控制线条样式 color: 'auto' } }, splitLine: { // 分隔线 length:20, // 属性length控制线长 lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式 color: 'auto' } }, pointer: { width:5 }, title: { offsetCenter: [0, '-30%'], // x, y,单位px }, detail: { textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE fontWeight: 'bolder' } }, data:[{value: 41, name: ' 西部地区\n 用水量', textStyle:{color:"#ffff00"} }] }, { name: '下降', type: 'gauge', center : ['85%', '62%'], // 默认全局居中 radius : '25%', min: 0, max: 2, startAngle: 315, endAngle: 225, splitNumber: 2, axisLine: { // 坐标轴线 lineStyle: { // 属性lineStyle控制线条样式 width: 8 } }, axisTick: { // 坐标轴小标记 show: false }, axisLabel: { formatter:function(v){ switch (v + '') { case '0' : return '0'; case '1' : return '下降'; case '2' : return '2.5%'; } } }, splitLine: { // 分隔线 length: 15, // 属性length控制线长 lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式 color: 'auto' } }, pointer: { width:2 }, title: { show: false }, detail: { show: false }, data:[{value: 2, name: '下降'}] } ] }; /* app.timeTicket = setInterval(function (){ myChart.setOption(option,true); },2000); */ myChart.setOption(option); </script> </body> </html> 修改核心代码:title : { textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE fontWeight: 'bolder', fontSize: 20, color:"#7FFFD4" } }, detail : { textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE fontWeight: 'bolder' } }, data: { value: 109.2, name: '\n\n 东部地区\n 用水量'}] }, 核心代码如下所示: 7.柱状图文本鼠标浮动上的颜色设置 需要修改的内容如下图所示: 代码如下所示: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <script src="echarts.min.js"></script> </head> <body> <div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript"> var myChart = echarts.init(document.getElementById('main')); var labelRight = { normal: { position: 'right' } }; var labelRight = { normal: { position: 'right' } }; var option = { title: { text: ' 十大高耗水行业用水量八减两增 ', subtext: '同比百分比(%)', }, tooltip : { trigger: 'axis', axisPointer : { // 坐标轴指示器,坐标轴触发有效 type : 'shadow' // 默认为直线,可选为:'line' | 'shadow' } }, grid: { top: 80, bottom: 80 }, xAxis: { type : 'value', position: 'top', splitLine: {lineStyle:{type:'dashed'}}, max:'4', }, yAxis: { type : 'category', axisLine: {show: false}, axisLabel: {show: false}, axisTick: {show: false}, splitLine: {show: false}, data : ['石油加工、炼焦和核燃料加工业' , '非金属矿物制品业', '化学原料和化学制品制造业', '有色金属冶炼和压延加工业', '造纸和纸制品业', '纺织业', '电力、热力生产和供应业', '非金属矿采选业', '黑色金属冶炼和压延加工业', '煤炭开采和洗选业'] }, series : [ { name:'幅度 ', type:'bar', stack: '总量', label: { normal: { show: true, formatter: '{b}' } }, data:[ {value: 0.2, label: labelRight, itemStyle:{ normal:{color:'gray'} } }, {value: 0.7, label: labelRight}, {value: -1.1, label: labelRight}, {value: -1.3, label: labelRight}, {value: -1.9, label: labelRight, itemStyle:{ normal: { color:'#28c6de', label: {textStyle:{color:'#00ff00'}} } } }, {value: -2.9, label: labelRight}, {value: -3.0, label: labelRight}, {value: -4.2, label: labelRight}, {value: -4.9, label: labelRight}, {value: -5.8, label: labelRight}, ] } ] }; myChart.setOption(option); </script> </body> </html> 核心代码:data:[ {value: 0.2, label: labelRight, itemStyle:{ normal:{color:'gray'} } }, {value: 0.7, label: labelRight}, {value: -1.1, label: labelRight}, {value: -1.3, label: labelRight}, {value: -1.9, label: labelRight, itemStyle:{ normal: { color:'#28c6de', label: {textStyle:{color:'#00ff00'}} } } }, {value: -2.9, label: labelRight}, {value: -3.0, label: labelRight}, {value: -4.2, label: labelRight}, {value: -4.9, label: labelRight}, {value: -5.8, label: labelRight}, ] 输出结果: 自适应大小,添加如下代码: // 为echarts对象加载数据 myChart.setOption(option); // 加上这一句即可 window.onresize = myChart.resize; 或者: window.addEventListener("resize",function(){ option.chart.resize(); }); 最后文章对你有所帮助,和学生相处就是不错,但enjoy myself~ (By:Eastmount 2016-10-17 中午1点半 )
今天主要讲述的内容是关于一元线性回归的知识,Python实现,包括以下内容: 1.机器学习常用数据集介绍 2.什么是线性回顾 3.LinearRegression使用方法 4.线性回归判断糖尿病 前文推荐: 【Python数据挖掘课程】一.安装Python及爬虫入门介绍 【Python数据挖掘课程】二.Kmeans聚类数据分析及Anaconda介绍 【Python数据挖掘课程】三.Kmeans聚类代码实现、作业及优化 【Python数据挖掘课程】四.决策树DTC数据分析及鸢尾数据集分析 希望这篇文章对你有所帮助,尤其是刚刚接触数据挖掘以及大数据的同学,同时准备尝试以案例为主的方式进行讲解。如果文章中存在不足或错误的地方,还请海涵~ 同时这篇文章是我上课的内容,所以参考了一些知识,强烈推荐大家学习斯坦福的机器学习Ng教授课程和Scikit-Learn中的内容。由于自己数学不是很好,自己也还在学习中,所以文章以代码和一元线性回归为主,数学方面的当自己学到一定的程度,才能进行深入的分享及介绍。抱歉~ 一. 数据集介绍 1.diabetes dataset数据集 数据集参考:http://scikit-learn.org/stable/datasets/ 这是一个糖尿病的数据集,主要包括442行数据,10个属性值,分别是:Age(年龄)、性别(Sex)、Body mass index(体质指数)、Average Blood Pressure(平均血压)、S1~S6一年后疾病级数指标。Target为一年后患疾病的定量指标。 输出如下所示: # -*- coding: utf-8 -*- """ Created on Thu Oct 27 02:37:05 2016 @author: yxz15 """ from sklearn import datasets diabetes = datasets.load_diabetes() #载入数据 print diabetes.data #数据 print diabetes.target #类标 print u'总行数: ', len(diabetes.data), len(diabetes.target) #数据总行数 print u'特征数: ', len(diabetes.data[0]) #每行数据集维数 print u'数据类型: ', diabetes.data.shape #类型 print type(diabetes.data), type(diabetes.target) #数据集类型 """ [[ 0.03807591 0.05068012 0.06169621 ..., -0.00259226 0.01990842 -0.01764613] [-0.00188202 -0.04464164 -0.05147406 ..., -0.03949338 -0.06832974 -0.09220405] ... [-0.04547248 -0.04464164 -0.0730303 ..., -0.03949338 -0.00421986 0.00306441]] [ 151. 75. 141. 206. 135. 97. 138. 63. 110. 310. 101. ... 64. 48. 178. 104. 132. 220. 57.] 总行数: 442 442 特征数: 10 数据类型: (442L, 10L) <type 'numpy.ndarray'> <type 'numpy.ndarray'> """ 2.sklearn常见数据集 常见的sklearn数据集包括,强烈推荐下面这篇文章: http://blog.csdn.net/sa14023053/article/details/52086695 sklearn包含一些不许要下载的toy数据集,见下表,包括波士顿房屋数据集、鸢尾花数据集、糖尿病数据集、手写字数据集和健身数据集等。 3.UCI数据集 常用数据集包括:http://archive.ics.uci.edu/ml/datasets.html 二. 什么是线性回归 1.机器学习简述 机器学习(Machine Learning )包括: a.监督学习(Supervised Learning):回归(Regression)、分类(Classification) 例:训练过程中知道结果。小孩给水果分类,给他苹果告诉他是苹果,反复训练学习。在给他说过,问他是什么?他回答准确,如果是桃子,他不能回答为苹果。 b.无监督学习(Unsupervised Learning):聚类(Clustering) 例:训练过程中不知道结果。给小孩一堆水果,如苹果、橘子、桃子,小孩开始不知道需要分类的水果是什么,让小孩对水果进行分类。分类完成后,给他一个苹果,小孩应该把它放到苹果堆中。 c.增强学习(Reinforcement Learning) 例:ML过程中,对行为做出评价,评价有正面的和负面两种。通过学习评价,程序应做出更好评价的行为。 d.推荐系统(Recommender System) 2.斯坦福公开课:第二课 单变量线性回归 这是NG教授的很著名的课程,这里主要引用52nlp的文章,真的太完美了。推荐阅读该作者的更多文章: Coursera公开课笔记: 斯坦福大学机器学习第二课"单变量线性回归(Linear regression with one variable)" <1>模型表示(Model Representation) 房屋价格预测问题,有监督学习问题。每个样本的输入都有正确输出或答案,它也是一个回归问题,预测一个真实值的输出。 训练集表示如下: 对于房价预测问题,讯息过程如下所示: 其中x代表房屋的大小,y代表预测的价格,h(hypothesis)将输入变量映射到输出变量y中,如何表示h呢?可以表示如下公式,简写为h(x),即带一个变量的线性回归或单变量线性回归问题。 <2>成本函数(Cost Function) 对于上面的公式函数h(x),如何求theta0和theta1参数呢? 构想: 对于训练集(x, y),选取参数0, 1使得hθ(x)尽可能的接近y。如何做呢?一种做法就是求训练集的平方误差函数(squared error function)。 Cost Function可表示为: 并且选取合适的参数使其最小化,数学表示如下: 总的来说,线性回归主要包括一下四个部分,分别是Hypothesis、Parameters、Cost Function、Goal。右图位简化版,theta0赋值为0。 然后令1分别取1、0.5、-0.5等值,同步对比hθ(x)和J(θ0,θ1)在二维坐标系中的变化情况,具体可参考原PPT中的对比图,很直观。 <3>梯度下降(Gradient descent) 应用的场景之一最小值问题: 对于一些函数,例如J(θ0,θ1) 目标: minθ0,θ1J(θ0,θ1) 方法的框架: a. 给0, 1一个初始值,例如都等于0; b. 每次改变0, 1的时候都保持J(θ0,θ1)递减,直到达到一个我们满意的最小值; 对于任一J(θ0,θ1) , 初始位置不同,最终达到的极小值点也不同,例如以下例子: 3.一元回归模型 转自文章:http://blog.sina.com.cn/s/blog_68c81f3901019hhp.html <1>什么是线性回归? 回归函数的具体解释和定义,可查看任何一本“概率论与数理统计”的书。我看的是“陈希孺”的。 这里我讲几点: 1)统计回归分析的任务,就在于根据 x1,x2,...,xp 线性回归和Y的观察值,去估计函数f,寻求变量之间近似的函数关系。 2)我们常用的是,假定f函数的数学形式已知,其中若干个参数未知,要通过自变量和因变量的观察值去估计未知的参数值。这叫“参数回归”。其中应用最广泛的是f为线性函数的假设: 这种情况叫做“线性回归”。 3)自变量只有一个时,叫做一元线性回归。 f(x) = b0+b1x 自变量有多个时,叫做多元线性回归。 f(x1,x2,...,xp) = b0 + b1x1 + b2x2 + ... + bpxp 4)分类(Classification)与回归(Regression)都属于监督学习,他们的区别在于: 分类:用于预测有限的离散值,如是否得了癌症(0,1),或手写数字的判断,是0,1,2,3,4,5,6,7,8还是9等。分类中,预测的可能的结果是有限的,且提前给定的。 回归:用于预测实数值,如给定了房子的面积,地段,和房间数,预测房子的价格。 <2>一元线性回归 假设:我们要预测房价。当前自变量(输入特征)是房子面积x,因变量是房价y.给定了一批训练集数据。我们要做的是利用手上的训练集数据,得出x与y之间的函数f关系,并用f函数来预测任意面积x对应的房价。 假设x与y是线性关系,则我们可以接着假设一元线性回归函数如下来代表y的预测值: 我们有训练集了,那么问题就成了如何利用现有的训练集来判定未知参数 (θ0,θ1) 的值,使其让h的值更接近实际值y? 训练集指的是已知x,y值的数据集合! 一种方法是计算它的成本函数(Cost function),即预测出来的h的值与实际值y之间的方差的大小来决定当前的(θ0,θ1)值是否是最优的! 常用的成本函数是最小二乘法: <3>模型总结 整个一元线性回归通过下面这张图总结即可: 参考文章:斯坦福大学机器学习——线性回归(Linear Regression) 最后,梯度下降和多元回归模型将继续学习,当我学到一定程度,再进行分享。 http://www.52nlp.cn/coursera公开课笔记-斯坦福大学机器学习第四课多变量 三. LinearRegression使用方法 LinearRegression模型在Sklearn.linear_model下,它主要是通过fit(x,y)的方法来训练模型,其中x为数据的属性,y为所属类型。 sklearn中引用回归模型的代码如下: from sklearn import linear_model #导入线性模型 regr = linear_model.LinearRegression() #使用线性回归 print regr 输出的函数原型如下所示: LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False) fit(x, y): 训练。分析模型参数,填充数据集。其中x为特征,y位标记或类属性。 predict(): 预测。它通过fit()算出的模型参数构成的模型,对解释变量进行预测其类属性。预测方法将返回预测值y_pred。 这里推荐"搬砖小工053"大神的文章,非常不错,强烈推荐。 引用他文章的例子,参考:scikit-learn : 线性回归,多元回归,多项式回归# -*- coding: utf-8 -*- """ Created on Fri Oct 28 00:44:55 2016 @author: yxz15 """ from sklearn import linear_model #导入线性模型 import matplotlib.pyplot as plt #绘图 import numpy as np #X表示匹萨尺寸 Y表示匹萨价格 X = [[6], [8], [10], [14], [18]] Y = [[7], [9], [13], [17.5], [18]] print u'数据集X: ', X print u'数据集Y: ', Y #回归训练 clf = linear_model.LinearRegression() #使用线性回归 clf.fit(X, Y) #导入数据集 res = clf.predict(np.array([12]).reshape(-1, 1))[0] #预测结果 print(u'预测一张12英寸匹萨价格:$%.2f' % res) #预测结果 X2 = [[0], [10], [14], [25]] Y2 = clf.predict(X2) #绘制线性回归图形 plt.figure() plt.title(u'diameter-cost curver') #标题 plt.xlabel(u'diameter') #x轴坐标 plt.ylabel(u'cost') #y轴坐标 plt.axis([0, 25, 0, 25]) #区间 plt.grid(True) #显示网格 plt.plot(X, Y, 'k.') #绘制训练数据集散点图 plt.plot(X2, Y2, 'g-') #绘制预测数据集直线 plt.show() 运行结果如下所示,首先输出数据集,同时调用sklearn包中的LinearRegression()回归函数,fit(X, Y)载入数据集进行训练,然后通过predict()预测数据12尺寸的匹萨价格,最后定义X2数组,预测它的价格。数据集X: [[6], [8], [10], [14], [18]] 数据集Y: [[7], [9], [13], [17.5], [18]] 预测一张12英寸匹萨价格:$13.68 输出的图形如下所示: 线性模型的回归系数W会保存在他的coef_方法中,截距保存在intercept_中。score(X,y,sample_weight=None) 评分函数,返回一个小于1的得分,可能会小于0。 print u'系数', clf.coef_ print u'截距', clf.intercept_ print u'评分函数', clf.score(X, Y) ''' 系数 [[ 0.9762931]] 截距 [ 1.96551743] 评分函数 0.910001596424 ''' 其中具体的系数介绍推荐如下资料:sklearn学习笔记之简单线性回归 - Magle 四. 线性回归判断糖尿病 1.Diabetes数据集(糖尿病数据集) 糖尿病数据集包含442个患者的10个生理特征(年龄,性别、体重、血压)和一年以后疾病级数指标。 然后载入数据,同时将diabetes糖尿病数据集分为测试数据和训练数据,其中测试数据为最后20行,训练数据从0到-20行(不包含最后20行),即diabetes.data[:-20]。 from sklearn import datasets #数据集 diabetes = datasets.load_diabetes() #载入数据 diabetes_x = diabetes.data[:, np.newaxis] #获取一个特征 diabetes_x_temp = diabetes_x[:, :, 2] diabetes_x_train = diabetes_x_temp[:-20] #训练样本 diabetes_x_test = diabetes_x_temp[-20:] #测试样本 后20行 diabetes_y_train = diabetes.target[:-20] #训练标记 diabetes_y_test = diabetes.target[-20:] #预测对比标记 print u'划分行数:', len(diabetes_x_temp), len(diabetes_x_train), len(diabetes_x_test) print diabetes_x_test 输出结果如下所示,可以看到442个数据划分为422行进行训练回归模型,20行数据用于预测。输出的diabetes_x_test共20行数据,每行仅一个特征。划分行数: 442 422 20 [[ 0.07786339] [-0.03961813] [ 0.01103904] [-0.04069594] [-0.03422907] [ 0.00564998] [ 0.08864151] [-0.03315126] [-0.05686312] [-0.03099563] [ 0.05522933] [-0.06009656] [ 0.00133873] [-0.02345095] [-0.07410811] [ 0.01966154] [-0.01590626] [-0.01590626] [ 0.03906215] [-0.0730303 ]] 2.完整代码 改代码的任务是从生理特征预测疾病级数,但仅获取了一维特征,即一元线性回归。【线性回归】的最简单形式给数据集拟合一个线性模型,主要是通过调整一系列的参以使得模型的残差平方和尽量小。 线性模型:y = βX+b X:数据 y:目标变量 β:回归系数 b:观测噪声(bias,偏差) 参考文章:Linear Regression Example - Scikit-Learn # -*- coding: utf-8 -*- """ Created on Fri Oct 28 01:21:30 2016 @author: yxz15 """ from sklearn import datasets import matplotlib.pyplot as plt import numpy as np #数据集 diabetes = datasets.load_diabetes() #载入数据 #获取一个特征 diabetes_x_temp = diabetes.data[:, np.newaxis, 2] diabetes_x_train = diabetes_x_temp[:-20] #训练样本 diabetes_x_test = diabetes_x_temp[-20:] #测试样本 后20行 diabetes_y_train = diabetes.target[:-20] #训练标记 diabetes_y_test = diabetes.target[-20:] #预测对比标记 #回归训练及预测 clf = linear_model.LinearRegression() clf.fit(diabetes_x_train, diabetes_y_train) #注: 训练数据集 #系数 残差平法和 方差得分 print 'Coefficients :\n', clf.coef_ print ("Residual sum of square: %.2f" %np.mean((clf.predict(diabetes_x_test) - diabetes_y_test) ** 2)) print ("variance score: %.2f" % clf.score(diabetes_x_test, diabetes_y_test)) #绘图 plt.title(u'LinearRegression Diabetes') #标题 plt.xlabel(u'Attributes') #x轴坐标 plt.ylabel(u'Measure of disease') #y轴坐标 #点的准确位置 plt.scatter(diabetes_x_test, diabetes_y_test, color = 'black') #预测结果 直线表示 plt.plot(diabetes_x_test, clf.predict(diabetes_x_test), color='blue', linewidth = 3) plt.show() 运行结果如下所示,包括系数、残差平方和、方差分数。 Coefficients :[ 938.23786125] Residual sum of square: 2548.07 variance score: 0.47 绘制图形如下所示,每个点表示真实的值,而直线表示预测的结果,比较接近吧。 同时绘制图形时,想去掉坐标具体的值,可增加如下代码: plt.xticks(()) plt.yticks(()) 五. 优化代码 下面是优化后的代码,增加了斜率、 截距的计算,同时增加了点图到线性方程的距离,保存图片设置像素。 # -*- coding: utf-8 -*- """ Created on Thu Dec 29 12:47:58 2011 @author: Administrator """ #第一步 数据集划分 from sklearn import datasets import numpy as np #获取数据 10*442 d = datasets.load_diabetes() x = d.data print u'获取x特征' print len(x), x.shape print x[:4] #获取一个特征 第3列数据 x_one = x[:,np.newaxis, 2] print x_one[:4] #获取的正确结果 y = d.target print u'获取的结果' print y[:4] #x特征划分 x_train = x_one[:-42] x_test = x_one[-42:] print len(x_train), len(x_test) y_train = y[:-42] y_test = y[-42:] print len(y_train), len(y_test) #第二步 线性回归实现 from sklearn import linear_model clf = linear_model.LinearRegression() print clf clf.fit(x_train, y_train) pre = clf.predict(x_test) print u'预测结果' print pre print u'真实结果' print y_test #第三步 评价结果 cost = np.mean(y_test-pre)**2 print u'次方', 2**5 print u'平方和计算:', cost print u'系数', clf.coef_ print u'截距', clf.intercept_ print u'方差', clf.score(x_test, y_test) #第四步 绘图 import matplotlib.pyplot as plt plt.title("diabetes") plt.xlabel("x") plt.ylabel("y") plt.plot(x_test, y_test, 'k.') plt.plot(x_test, pre, 'g-') for idx, m in enumerate(x_test): plt.plot([m, m],[y_test[idx], pre[idx]], 'r-') plt.savefig('power.png', dpi=300) plt.show() 运行结果如下所示:获取x特征 442 (442L, 10L) [[ 0.03807591 0.05068012 0.06169621 0.02187235 -0.0442235 -0.03482076 -0.04340085 -0.00259226 0.01990842 -0.01764613] [-0.00188202 -0.04464164 -0.05147406 -0.02632783 -0.00844872 -0.01916334 0.07441156 -0.03949338 -0.06832974 -0.09220405] [ 0.08529891 0.05068012 0.04445121 -0.00567061 -0.04559945 -0.03419447 -0.03235593 -0.00259226 0.00286377 -0.02593034] [-0.08906294 -0.04464164 -0.01159501 -0.03665645 0.01219057 0.02499059 -0.03603757 0.03430886 0.02269202 -0.00936191]] [[ 0.06169621] [-0.05147406] [ 0.04445121] [-0.01159501]] 获取的结果 [ 151. 75. 141. 206.] 400 42 400 42 LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False) 预测结果 [ 196.51241167 109.98667708 121.31742804 245.95568858 204.75295782 270.67732703 75.99442421 241.8354155 104.83633574 141.91879342 126.46776938 208.8732309 234.62493762 152.21947611 159.42995399 161.49009053 229.47459628 221.23405012 129.55797419 100.71606266 118.22722323 168.70056841 227.41445974 115.13701842 163.55022706 114.10695016 120.28735977 158.39988572 237.71514243 121.31742804 98.65592612 123.37756458 205.78302609 95.56572131 154.27961264 130.58804246 82.17483382 171.79077322 137.79852034 137.79852034 190.33200206 83.20490209] 真实结果 [ 175. 93. 168. 275. 293. 281. 72. 140. 189. 181. 209. 136. 261. 113. 131. 174. 257. 55. 84. 42. 146. 212. 233. 91. 111. 152. 120. 67. 310. 94. 183. 66. 173. 72. 49. 64. 48. 178. 104. 132. 220. 57.] 次方 32 平方和计算: 83.192340827 系数 [ 955.70303385] 截距 153.000183957 方差 0.427204267067 绘制图形如下所示: 强烈推荐下面线性回归相关的文章,希望读者自行阅读: [译]针对科学数据处理的统计学习教程(scikit-learn教程2)Tacey Wong (重点) scikit-learn : 线性回归 - 搬砖小工053 结合Scikit-learn介绍几种常用的特征选择方法 - Bryan 用Python开始机器学习(3:数据拟合与广义线性回归) - lsldd Scikit Learn: 在python中机器学习 - yyliu Python机器学习——线性模型 - 郝智恒 sklearn 数据加载工具(1) - 搬砖小工053 sklearn系列之----线性回归 - Gavin__Zhou 希望文章对你有所帮助,上课内容还需要继续探索,这篇文章更希望你关注的是Python代码如何实现的,因为数学不好,所以详细的推导过程,建议看文中的链接。 (By:Eastmount 2016-10-28 半夜3点半 )
前一篇"[网站搭建] 阿里云虚拟主机搭建及FTP文件上传"主要讲述了如何通过阿里云虚拟机搭建网站服务器,同时FTP上传文件,登录后进入控制台或管理界面,接下来的主要步骤如下图所示: 1.获取追加信息 2.网站备案 3.上传网站数据库数据 4.网站调试 5.域名解析 6.域名绑定 从域名到网站,只需四步,轻松访问,同时需要注意网站备案。 一. 购买域名 临时域名 bxw2442620243.my3w.com 会提示错误: 然后我购买了个域名,具体步骤如下所示。 详情参考:https://help.aliyun.com/knowledge_detail/35895.html 查域名并购买注册。 购买个人域名。 点击"域名"->"信息模块"->"创建新的信息模块",右上角。 填写信息并进行实名注册。 认证成功后,再选择绑定及支付,这样就注册成功过了。 二. 网站备案 备案流程参考:https://beian.aliyun.com/ 主要包括:1.登录备案系统;2.填写备案信息;3.提交初审;4.办理拍照;5.等待管局审核。 首次备案图文引导,参考文章:首次备案 1.注册并登录阿里云ICP代理备案管理系统 然后登录系统进行注册:阿里云 ICP代备案管理系统 注册成功后,进行登录。 然后点击登录,登录地址:https://beian.gein.cn/account/login.htm 2.填写主办单位信息 开始备案,并记住"备案ID":3137XXX,备案个体工商户无字号。 我申请的域名:www.eastmountyxz.com 点击"验证"按钮,注意主机管理控制台用户名即前面的云虚拟机。 然后填写主板单位信息,注意个体工商户无字号和公章。 3.填写网站信息 填写网站信息,包括网站名称、URL、备注内容等。 4.上传资料 然后上传资料,包括身份证。 其中《网站备案信息真实性核验单》需要下载打印签字,再上传图片。 上传居住证,但是我没有上传的一卡通,后来电话提醒我,让我上传了户口首页及自己的那一页,主要证明你所在地址。 上传资料就基本完成了。这过程中会有人电话联系你,需要保持电话畅通,同时暂时不要访问申请的域名。 5.办理拍照 我电话通知我让我等待,就以为不需要拍照了,所以耽误了些时间。 办理拍照流程:https://help.aliyun.com/knowledge_detail/36968.html 登录备案系统,点击"申请幕布"。 然后填写申请幕布的信息,两三天就送来幕布,d得到幕布拍照时,请先查看拍照说明,拍照完成可登陆备案系统,点击【上传照片】将核验照片上传审核。 用手机拍半身照上传就行。 然后等待照片审核,这个过程也很快的,1个工作日就行。 6.提交管局 它审核成功后,阿里巴巴会帮你讲信息提交到当地省通信管理局。期间会电话确认你的个人信息,主要是看是不是本人申请的网站。 管局通过审核后,备案就完成了,此时它会电话通知的。域名如下: 三. 域名解析 登陆阿里云/万网【管理控制台】,进入域名解析列表,把域名指向网站主机 IP 地址(或电子邮箱 MX 记录),即可快速完成域名解析设置。具体操作流程如下: 1.登录阿里云管理控制台 地址:https://account.aliyun.com/login/login.htm 点击右上角"控制台"。 主要包括设置域名、云解析DNS、允虚拟主机、企业邮箱。 2.域名修改介绍(解析后) 点击"域名"进入下图界面,显示我的域名"eastmountyxz.com"。 点击"解析"进入下图界面,可以"修改"或"添加解析"。 3.云虚拟主机修改介绍(解析后) 点击"云虚拟主机"进入下图界面,显示信息: 主机名:bxw2442620243, IP地址:60.205.24.36,主机域名等。 点击右边"管理"按钮,进入下图界面,可以修订各种信息及密码。 4.域名解析 登陆阿里云/万网【管理控制台】,在顶部主导航位置点击【产品与服务】--【云解析】,进入“域名解析列表”;选择需添加解析的域名,点击右侧操作的【解析】入口,即可进入到域名解析设置页。 点击"解析"按钮后进入如下所示界面,点击"新手引导设置"。 进入"新手设置引导"界面厚,点击"设置网站解析-立即设置"。 然后将域名解析到我的万维网主机,即:bxw2442620243(IP:60.205.24.36)。 解析成功后如下图所示,同时查看云虚拟主机,ip对应两个域名,其中一个是临时域名,新增域名"eastmountyxz.com"也增加了。 四. 绑定域名及访问网站 域名解析成功厚,需要通过域名绑定,将域名和云虚拟主机或与服务器绑定在一起,才能访问网站内容。 绑定云虚拟主机域名参考:绑定云虚拟主机域名 云虚拟主机设置及FTP文件上传参考:阿里云虚拟主机搭建及FTP文件上传 1.登录阿里云管理控制台 地址:https://account.aliyun.com/login/login.htm 点击右上角"控制台",点击"云解析DNS"链接。 2.云虚拟主机 点击"云虚拟主机"进入下图界面,显示信息: 主机名:bxw2442620243, IP地址:60.205.24.36,主机域名等。 3.进入"主机管理控制台"界面 主机管理控制台,基础环境设置 > 域名绑定,在 输入新域名 单击"添加"进行绑定。显示"成功"和"已备案"就表示成功。 同时点击"操作日志",可以看到具体的操作记录。 在这期间会报错,这是因为域名没有绑定,尤其是需要新增www的域名。 然后上传FTP文件,然后进行访问,如下图在文件中输入ftp地址即可访问。 输入密码后如下所示: 访问网址如下所示:http://www.eastmountyxz.com/index.html 再如访问最近指导学生的可视化比赛的界面。 最后文章对你有所帮助,可能你会觉得很简单或者阿里巴巴官网很详细了,但是这毕竟是结合自己的搭建过程进行的,如果存在不足之处,还请海涵~感谢阿里云和云栖社区。 (By:Eastmount 2016-10-23 凌晨4点半 )
这篇文章直接给出上次关于Kmeans聚类的篮球远动员数据分析案例,同时介绍这次作业同学们完成的图例,最后介绍Matplotlib包绘图的优化知识。 前文推荐: 【Python数据挖掘课程】一.安装Python及爬虫入门介绍 【Python数据挖掘课程】二.Kmeans聚类数据分析及Anaconda介绍 希望这篇文章对你有所帮助,尤其是刚刚接触数据挖掘以及大数据的同学,同时准备尝试以案例为主的方式进行讲解。如果文章中存在不足或错误的地方,还请海涵~ 一. 案例实现 这里不再赘述,详见第二篇文章,直接上代码,这是我的学生完成的作业。 数据集: 下载地址:KEEL-dataset - Basketball data set 篮球运动员数据,每分钟助攻和每分钟得分数。通过该数据集判断一个篮球运动员属于什么位置(控位、分位、中锋等)。完整数据集包括5个特征,每分钟助攻数、运动员身高、运动员出场时间、运动员年龄和每分钟得分数。 assists_per_minute height time_played age points_per_minute 0 0.0888 201 36.02 28 0.5885 1 0.1399 198 39.32 30 0.8291 2 0.0747 198 38.80 26 0.4974 3 0.0983 191 40.71 30 0.5772 4 0.1276 196 38.40 28 0.5703 5 0.1671 201 34.10 31 0.5835 6 0.1906 193 36.20 30 0.5276 7 0.1061 191 36.75 27 0.5523 8 0.2446 185 38.43 29 0.4007 9 0.1670 203 33.54 24 0.4770 10 0.2485 188 35.01 27 0.4313 11 0.1227 198 36.67 29 0.4909 12 0.1240 185 33.88 24 0.5668 13 0.1461 191 35.59 30 0.5113 14 0.2315 191 38.01 28 0.3788 15 0.0494 193 32.38 32 0.5590 16 0.1107 196 35.22 25 0.4799 17 0.2521 183 31.73 29 0.5735 18 0.1007 193 28.81 34 0.6318 19 0.1067 196 35.60 23 0.4326 20 0.1956 188 35.28 32 0.4280 完整代码:# -*- coding: utf-8 -*- from sklearn.cluster import Birch from sklearn.cluster import KMeans X = [[0.0888, 0.5885], [0.1399, 0.8291], [0.0747, 0.4974], [0.0983, 0.5772], [0.1276, 0.5703], [0.1671, 0.5835], [0.1906, 0.5276], [0.1061, 0.5523], [0.2446, 0.4007], [0.1670, 0.4770], [0.2485, 0.4313], [0.1227, 0.4909], [0.1240, 0.5668], [0.1461, 0.5113], [0.2315, 0.3788], [0.0494, 0.5590], [0.1107, 0.4799], [0.2521, 0.5735], [0.1007, 0.6318], [0.1067, 0.4326], [0.1956, 0.4280] ] print X # Kmeans聚类 clf = KMeans(n_clusters=3) y_pred = clf.fit_predict(X) print(clf) print(y_pred) import numpy as np import matplotlib.pyplot as plt x = [n[0] for n in X] print x y = [n[1] for n in X] print y # 可视化操作 plt.scatter(x, y, c=y_pred, marker='x') plt.title("Kmeans-Basketball Data") plt.xlabel("assists_per_minute") plt.ylabel("points_per_minute") plt.legend(["Rank"]) plt.show() 运行结果: 从图中可以看到聚集成三类,红色比较厉害,得分很高;中间蓝色是一类,普通球员;右小角绿色是一类,助攻高得分低,是控位。 代码分析: from sklearn.cluster import KMeans 表示在sklearn中处理kmeans聚类问题,用到 sklearn.cluster.KMeans 这个类。X = [[164,62],[156,50],...] X是数据集,包括2列20行,即20个球员的助攻数和得分数。clf = KMeans(n_clusters=3) 表示输出完整Kmeans函数,包括很多省略参数,将数据集分成类簇数为3的聚类。y_pred =clf.fit_predict(X) 输出聚类预测结果,对X聚类,20行数据,每个y_pred对应X的一行或一个孩子,聚成3类,类标为0、1、2。print(y_pred) 输出结果:[0 2 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1]import matplotlib.pyplot as plt matplotlib.pyplot是用来画图的方法,matplotlib是可视化包。x = [n[0] for n in X] y = [n[1] for n in X] 获取第1列的值, 使用for循环获取 ,n[0]表示X第一列。 获取第2列的值,使用for循环获取 ,n[1]表示X第2列。plt.scatter(x, y, c=y_pred, marker='o') 绘制散点图(scatter),横轴为x,获取的第1列数据;纵轴为y,获取的第2列数据;c=y_pred对聚类的预测结果画出散点图,marker='o'说明用点表示图形。plt.title("Kmeans-Basketball Data") 表示图形的标题为Kmeans-heightweight Data。plt.xlabel("assists_per_minute") 表示图形x轴的标题。plt.ylabel("points_per_minute") 表示图形y轴的标题。plt.legend(["Rank"]) 设置右上角图例。plt.show() 表示显示图形。 二. 学生图例 下面简单展示学生做的作业及分析,感觉还是不错,毕竟才上几节课而且第一次作业,希望后面的作业更加精彩吧。因为学生的专业分布不同,所以尽量让学生设计他们专业的内容。 eg 遗传学身高体重数据 第一列表示孩子的身高,单位cm;第二列表示孩子的体重,单位kg。从上图可以看出,数据集被分为了三类。绿色为一类、蓝色为一类,红色为一类。 eg 微博数据集 第一列代表微博中某条信息的转发量,第二列代表微博中某条信息的评论数。从上图可以看出,总共分为3类,共三种颜色,绿色一层说明该信息转发量与评论数都很高。 eg 上市公司财务报表 第一列表示公司利润率;第二列表示公司资产规模。从上图可以看出,总共分为4类,共四种颜色。暗红色为资产规模最大,依次至蓝色资产规模减小。 eg 世界各国家人均面积与土地面积 第一列表示各国家的人均面积(人/ 平方公里);第二列表示各国家的土地面积(万平方公里)。从上图可以看出,总共分为3类,共三种颜色。红色表示的国家相对来说最拥挤,可能是孟加拉这样土地面积少且人口众多的国家;蓝色就是地广人稀的代表,比如俄罗斯、美国、、墨西哥、巴西;绿色表示人口密度分布比较平均的国家。 eg employee salaries数据集 第一列表示员工工资;第二列表示员工年龄数。从上图可以看出,总共分为5类,共5种颜色。总体呈现正相关性,年龄越大,工资越高;除个别外,总体正线性关系。 eg 学生英语成绩数据集 第一列表示学生英语平时成绩;第二列表示学生英语期末成绩。从上图可以看出,总共分为4类,共四种颜色。黄色一层,平时成绩和末考成绩都很高,属于“学霸”级别的人物;其次,蓝色一层和红色一层;最后,天蓝色一层,暂且称之为“学渣”。 三. Matplotlib绘图优化 Matplotlib代码的优化: 1.第一部分代码是定义X数组,实际中是读取文件进行的,如何实现读取文件中数据再转换为矩阵进行聚类呢? 2.第二部分是绘制图形,希望绘制不同的颜色及类型,使用legend()绘制图标。 假设存在数据集如下图所示:data.txt 数据集包括96个运动员的数据,源自:KEEL-dataset - Basketball data set 现需要获取第一列每分钟助攻数、第五列每分钟得分数存于矩阵中。0.0888 201 36.02 28 0.5885 0.1399 198 39.32 30 0.8291 0.0747 198 38.8 26 0.4974 0.0983 191 40.71 30 0.5772 0.1276 196 38.4 28 0.5703 0.1671 201 34.1 31 0.5835 0.1906 193 36.2 30 0.5276 0.1061 191 36.75 27 0.5523 0.2446 185 38.43 29 0.4007 0.167 203 33.54 24 0.477 0.2485 188 35.01 27 0.4313 0.1227 198 36.67 29 0.4909 0.124 185 33.88 24 0.5668 0.1461 191 35.59 30 0.5113 0.2315 191 38.01 28 0.3788 0.0494 193 32.38 32 0.559 0.1107 196 35.22 25 0.4799 0.2521 183 31.73 29 0.5735 0.1007 193 28.81 34 0.6318 0.1067 196 35.6 23 0.4326 0.1956 188 35.28 32 0.428 0.1828 191 29.54 28 0.4401 0.1627 196 31.35 28 0.5581 0.1403 198 33.5 23 0.4866 0.1563 193 34.56 32 0.5267 0.2681 183 39.53 27 0.5439 0.1236 196 26.7 34 0.4419 0.13 188 30.77 26 0.3998 0.0896 198 25.67 30 0.4325 0.2071 178 36.22 30 0.4086 0.2244 185 36.55 23 0.4624 0.3437 185 34.91 31 0.4325 0.1058 191 28.35 28 0.4903 0.2326 185 33.53 27 0.4802 0.1577 193 31.07 25 0.4345 0.2327 185 36.52 32 0.4819 0.1256 196 27.87 29 0.6244 0.107 198 24.31 34 0.3991 0.1343 193 31.26 28 0.4414 0.0586 196 22.18 23 0.4013 0.2383 185 35.25 26 0.3801 0.1006 198 22.87 30 0.3498 0.2164 193 24.49 32 0.3185 0.1485 198 23.57 27 0.3097 0.227 191 31.72 27 0.4319 0.1649 188 27.9 25 0.3799 0.1188 191 22.74 24 0.4091 0.194 193 20.62 27 0.3588 0.2495 185 30.46 25 0.4727 0.2378 185 32.38 27 0.3212 0.1592 191 25.75 31 0.3418 0.2069 170 33.84 30 0.4285 0.2084 185 27.83 25 0.3917 0.0877 193 21.67 26 0.5769 0.101 193 21.79 24 0.4773 0.0942 201 20.17 26 0.4512 0.055 193 29.07 31 0.3096 0.1071 196 24.28 24 0.3089 0.0728 193 19.24 27 0.4573 0.2771 180 27.07 28 0.3214 0.0528 196 18.95 22 0.5437 0.213 188 21.59 30 0.4121 0.1356 193 13.27 31 0.2185 0.1043 196 16.3 23 0.3313 0.113 191 23.01 25 0.3302 0.1477 196 20.31 31 0.4677 0.1317 188 17.46 33 0.2406 0.2187 191 21.95 28 0.3007 0.2127 188 14.57 37 0.2471 0.2547 160 34.55 28 0.2894 0.1591 191 22.0 24 0.3682 0.0898 196 13.37 34 0.389 0.2146 188 20.51 24 0.512 0.1871 183 19.78 28 0.4449 0.1528 191 16.36 33 0.4035 0.156 191 16.03 23 0.2683 0.2348 188 24.27 26 0.2719 0.1623 180 18.49 28 0.3408 0.1239 180 17.76 26 0.4393 0.2178 185 13.31 25 0.3004 0.1608 185 17.41 26 0.3503 0.0805 193 13.67 25 0.4388 0.1776 193 17.46 27 0.2578 0.1668 185 14.38 35 0.2989 0.1072 188 12.12 31 0.4455 0.1821 185 12.63 25 0.3087 0.188 180 12.24 30 0.3678 0.1167 196 12.0 24 0.3667 0.2617 185 24.46 27 0.3189 0.1994 188 20.06 27 0.4187 0.1706 170 17.0 25 0.5059 0.1554 183 11.58 24 0.3195 0.2282 185 10.08 24 0.2381 0.1778 185 18.56 23 0.2802 0.1863 185 11.81 23 0.381 0.1014 193 13.81 32 0.1593 代码如下:# -*- coding: utf-8 -*- """ By: Eastmount CSDN 2016-10-12 该部分讲数据集读取,然后赋值给X变量 读取文件data.txt 保存结果为X """ import os data = [] for line in open("data.txt", "r").readlines(): line = line.rstrip() #删除换行 #删除多余空格,保存一个空格连接 result = ' '.join(line.split()) #获取每行五个值 '0 0.0888 201 36.02 28 0.5885' 注意:字符串转换为浮点型数 s = [float(x) for x in result.strip().split(' ')] #输出结果:['0', '0.0888', '201', '36.02', '28', '0.5885'] print s #数据存储至data data.append(s) #输出完整数据集 print u'完整数据集' print data print type(data) ''' 现在输出数据集: ['0 0.0888 201 36.02 28 0.5885', '1 0.1399 198 39.32 30 0.8291', '2 0.0747 198 38.80 26 0.4974', '3 0.0983 191 40.71 30 0.5772', '4 0.1276 196 38.40 28 0.5703' ] ''' print u'第一列 第五列数据' L2 = [n[0] for n in data] print L2 L5 = [n[4] for n in data] print L5 ''' X表示二维矩阵数据,篮球运动员比赛数据 总共96行,每行获取两列数据 第一列表示球员每分钟助攻数:assists_per_minute 第五列表示球员每分钟得分数:points_per_minute ''' #两列数据生成二维数据 print u'两列数据合并成二维矩阵' T = dict(zip(L2,L5)) type(T) #dict类型转换为list print u'List' X = list(map(lambda x,y: (x,y), T.keys(),T.values())) print X print type(X) """ KMeans聚类 clf = KMeans(n_clusters=3) 表示类簇数为3,聚成3类数据,clf即赋值为KMeans y_pred = clf.fit_predict(X) 载入数据集X,并且将聚类的结果赋值给y_pred """ from sklearn.cluster import Birch from sklearn.cluster import KMeans clf = KMeans(n_clusters=3) y_pred = clf.fit_predict(X) print(clf) #输出聚类预测结果,96行数据,每个y_pred对应X一行或一个球员,聚成3类,类标为0、1、2 print(y_pred) """ 可视化绘图 Python导入Matplotlib包,专门用于绘图 import matplotlib.pyplot as plt 此处as相当于重命名,plt用于显示图像 """ import numpy as np import matplotlib.pyplot as plt #获取第一列和第二列数据 使用for循环获取 n[0]表示X第一列 x = [n[0] for n in X] print x y = [n[1] for n in X] print y #绘制散点图 参数:x横轴 y纵轴 c=y_pred聚类预测结果 marker类型 o表示圆点 *表示星型 x表示点 #plt.scatter(x, y, c=y_pred, marker='x') #坐标 x1 = [] y1 = [] x2 = [] y2 = [] x3 = [] y3 = [] #分布获取类标为0、1、2的数据 赋值给(x1,y1) (x2,y2) (x3,y3) i = 0 while i < len(X): if y_pred[i]==0: x1.append(X[i][0]) y1.append(X[i][1]) elif y_pred[i]==1: x2.append(X[i][0]) y2.append(X[i][1]) elif y_pred[i]==2: x3.append(X[i][0]) y3.append(X[i][1]) i = i + 1 #四种颜色 红 绿 蓝 黑 plot1, = plt.plot(x1, y1, 'or', marker="x") plot2, = plt.plot(x2, y2, 'og', marker="o") plot3, = plt.plot(x3, y3, 'ob', marker="*") #绘制标题 plt.title("Kmeans-Basketball Data") #绘制x轴和y轴坐标 plt.xlabel("assists_per_minute") plt.ylabel("points_per_minute") #设置右上角图例 plt.legend((plot1, plot2, plot3), ('A', 'B', 'C'), fontsize=10) plt.show() 输出结果如下图所示:三个层次很明显,而且右上角也标注。 可视化部分强烈推荐资料: 数字的可视化:python画图之散点图sactter函数详解 - hefei_cyp python 科学计算(一) - bovine Matplotlib scatter plot with legend - stackoverflow Python数据可视化——散点图 Rachel-Zhang(按评论修改代码) 关于Matlab作图的若干问题 - 张朋飞 四. Spyder常见问题 下面是常见遇到的几个问题: 1.Spyder软件如果Editor编辑框不在,如何调出来。 2.会缺少一些第三方包,如lda,如何导入。使用cd ..去到C盘根目录,cd去到Anaconda的Scripts目录下,输入"pip install selenium"安装selenium相应的包,"pip install lda"安装lda包。 学生告诉我另一个更方便的方法: 3.运行时报错,缺少Console,点击如下。 4.如果Spyder安装点击没有反应,重新安装也没有反应,建议在运行下试试。 实在不行卸载再重装:pip uninstall spyder pip install spyder 5.Spyder如何显示绘制Matplotlib中文。 from matplotlib.font_manager import FontProperties font = FontProperties(fname="C:\Windows\Fonts/msyh.ttf", size=10) #绘制标题 fontproperties表示字体类型,用于显示中文字符,下同 plt.title(u'世界各国家人均面积与土地面积',fontproperties=font) #绘制x轴和y轴坐标 plt.ylabel(u'人均面积(人/ 平方公里)',fontproperties=font) plt.xlabel(u'面积(万平方公里)',fontproperties=font) (By:Eastmount 2016-10-12 深夜3点半 )
最近发生的事情很多,其中一件很重要的事情就是:学生生涯的结束,教学生涯的开始。我准备下个月写一篇总结研究生生涯的文章,包括自己放弃互联网选择回家教书、找工作经历、项目和毕业设计的各种感想。很荣幸XB七月初就给了我第一次大学教学的经历,很享受很珍惜也很感触。尤其是看到学生“秀璋,你好!”的跑马灯欢迎界面、发表“终于会编程了,感觉自己好牛逼”的说说、递给我餐巾纸、和你们打球等等。 言归正传,因为学生是大二升大三的,但是却没有学过网页相关知识,所以准备给《JSP网站开发》这门小学期时间课程总结些HTML入门知识,这对网站设计很有用的,希望对你们也有所帮助吧!文章中如果有不足之处,还请海涵~ 目录: 一. JSP前端设计及HTML基础介绍 1.JSP基础知识 2.HTML基础介绍 3.查看网页源代码及审查元素 二. HTML常用标记符 1.HEAD标记符 2.HTML属性 3.BODY标记符及设置BODY背景图片颜色 4.注释 5.FONT标记符 6.B\I\U字体样式及物理元素与逻辑元素 7.特色字符<>引号等 三.HTML设置段落格式 1.分段标记符P 2.换行标记符BR 3.标题样式Hn 4.添加水平线HR 5.align属性设置对齐方式 6.列表知识 推荐大家通过W3C学习HTML知识,文章主要参考我上课内容及北理HTML课件。 一. JSP前端设计及HTML基础介绍 JSP网站开发推荐这六篇文章:Java+MyEclipse+Tomcat系列 参考前文: Java+MyEclipse+Tomcat (一)配置过程及jsp网站开发入门 Java+MyEclipse+Tomcat (二)配置Servlet及简单实现表单提交 Java+MyEclipse+Tomcat (三)配置MySQL及查询数据显示在JSP网页中 Java+MyEclipse+Tomcat (四)Servlet提交表单和数据库操作 Java+MyEclipse+Tomcat (五)DAO和Java Bean实现数据库和界面分开操作 Java+MyEclipse+Tomcat (六)详解Servlet和DAO数据库增删改查操作 免费资源下载地址: http://download.csdn.net/detail/eastmount/8733385 1.JSP基础知识 静态网页:指网页一旦制作完成,不能随意更改,缺点是不能实现用户与服务器之间的交互,网页制作成本高,更改困难,如“hao123”。 动态网页:能根据用户的要求而动态改变的网页内容,包括JSP、ASP、PHP,如“12306”。 JSP:英文是Java Server Page,Java服务器页面。JSP技术有点类似ASP技术,它是在传统网页HTML文件(*.html, *.htm)中插入Java程序段(Scriptlet)和JSP标记(Tag),从而形成JSP文件(*.jsp)。 在配置MyEclipse和Tomcat后运行如下图所示,其中WebRoot文件夹下的index.jsp即为网站的前端页面设计。 其中index.jsp的代码为:<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> This is my JSP page. <br> </body> </html> 这篇文章的重点部分是普及HTML的基础知识,并结合JSP开发进行普及介绍。 2.HTML基础知识 第一个问题你肯定会问“什么是HTML”? HTML是描述网页的一种语言,一种规范,超文本标记语言(HyperText Markup Language),并不是一种编程语言。主要通过HTML标记标签(markup tag)来标记要显示的各个部分。 HTML标签主要包括两个特性: (1)HTML是所有标记符都由尖括号包围起来,如<html>; (2)标签成对出现,开始标签或开放标签(Opening tag)是由“尖括号+关键字”组成,如<table>;而结束标签或闭合标签(Closing tag)是由“<+斜杠/+关键字+>”组成,如</table>。 HTML文档或网页:由HTML标签和纯文本组成。Web浏览器读取HTML网页,通过网页中添加的标记符,可以告诉浏览器如何显示网页,即确定内容的格式。然后根据内容周围的HTML标记符解析并显示各种内容。 如何编写HTML网页呢? 可以通过Dreamweaver编辑HTML,但个人建议使用Notepad记事本、Nptepad++、TextEdit、UltraEdit、Sublime Text等工具来书写网站。 运行直接将网站命名为.html或.htm后缀的文件,通过浏览器打开即可。例如:<html> <head> <title>HTML开发</title> </head> <body> 欢迎学习Eastmount的HTML网站开发知识! <br> </body> </html> 使用Sublime Text编辑及运行结果如下图所示,其所有内容放在<html></html>两个标记符之间。图中包含一个首部标记<head></head>,其中首部在JSP中通常可设置标题<title>和JavaScript;同时包含正文标记<body></body>可见网页内容,里面书写具体的网页内容。 3.查看网页源代码及审查元素 通过查看网页源代码学习别人的代码也是非常重要的一个内容。右键浏览器,查看源代码即可。 通过审查元素可以定位到具体的网页内容,在爬虫过程中很常见。定位具体的控件,然后右键审查元素。 显示百度Logo图片结果如下图所示: 注意:同样右键可以设置浏览器的编码方式,只要.html或.jsp与浏览器编码方式一致,就不会显示中文乱码,例如都为UTF-8。 二. HTML常用标记符 注意:HTML标记符是不区分大小写的,<html>和<HTML>没有区别,但是XHTML要求标记符区分大小写。希望大家养成使用小写的习惯,兼容性更好。 1.HEAD标记符 首部标记<head>和</head>位于Web页的开头,其中不包括Web页面的任何实际内容,而是提供一些与Web页有关的特定信息。首部标记内容通常包括: (1)样式表CSS定义位于<style>和</style>之间; (2)脚本定义位于<script>和</script>之间; (3)标题标记<title>和</tilte>之间,用于定义网页标题。如: 2.HTML属性 HTML中属性用于描述对象特征的特性。所有属性斗放置在开始标记符的尖括号内,多个属性使用空格分开,通常不区分大小写,以“名称-值”的形式出现。 例如:如<table name="t1" id="t2">。 属性值应该始终包括在引号内,常用双引号,某些情况下属性本省包含有双引号,必须使用单引号。例如在java中显示超链接。 常见属性: --class: 规定元素类名 --id: 规定元素唯一id --name: 规定元素名称 --style: 规定元素行内样式(inline style) --title: 规定元素额外信息(工具中提示) PS:会面讲到具体的标记符会详细介绍。 3.BODY标记符及设置BODY背景图片颜色 BODY正文标记符中的文字,如果没有其他标记符修饰,则为无格式形式。 注意:空格、回车这些格式控制在显示时都不起作用,如果要使用它们应使用预格式化元素<pre>和</pre>。 通常网页格式如下图所示,建议开始结束标签补齐再写具体内容。 设置<body>标记符的背景颜色使用bgcolor属性可以设置,text用于设置正文的颜色,颜色出来使用名称进行赋值外,可以使用#RRGGBB格式进行赋值。 --bgcolor: 设置背景颜色; --text: 设置背景正文字体颜色; --background: 设置背景图片; eg: <body bgcolor="yellow" text="red"> 使用background设置背景图案。 eg: <body background="image.jpg"> 注意1:背景图片会重复显示,默认是如果图片不能沾满整个屏幕,就在x方向和y方向重复显示。可以通过background-repeat设置重复属性,包括四个值:repeat默认,x和y方向重复;repeat-x水平方向重复;repeat-y垂直方向重复;no-repeat背景图像仅显示一次。 设置代码:<body style="background-image:url('图片地址'); background-repeat:no-repeat;"> 注意2:在JSP中建议将图片拖动到WebRoot目录下,或建立一个images文件夹,拖动到该项目中,然后通过相对路径进行调用,而不是通过设置"C:\\xxx\\xxx.jpg"的方式(容易丢失路径)。 4.注释 HTML中的注释开始标签是<!--,结束标签是-->。两个标记之间的内容为注释,不在浏览器中显示。 主要用于提高代码的可读性、易于理解。 例如:<!-- This is a comment --> 5.FONT标记符 设置字符格式,包括字体、字号、文字颜色等。 --size: 设置字体大小,值从1到7,默认是3,可使用+或-作为相对值; --color: 设置字体颜色; --face: 设置字体样式; 例如:设置字体大小和颜色。 <HTML> <HEAD><TITLE>FONT 标记符的 size color 属性示例</TITLE></HEAD> <BODY> <P>正常文本 <P><FONT size="7"> 这些是大字体的文本 </FONT> <P><FONT size="1"> 这些是小字体的文本 </FONT> <P><FONT size="+2">这些文字的字体比正常文本大 2 号</FONT> <P><FONT size="-2">这些文字的字体比正常文本小 2 号</FONT> <P><FONT color="red">这些文字是红色字体</FONT> <P><FONT color="green">这些文字是绿色字体</FONT> <P><FONT color="#334455">这些文字是#334455色字体</FONT> </BODY> </HTML> 显示效果如下图所示: 例如:设置字体样式。<HTML> <HEAD><TITLE>字体示例</TITLE></HEAD> <BODY> <DIV align="center"> <P>以下是常用中文字体:</P> <FONT face="宋体">宋体</FONT><BR> <FONT face="楷体_GB2312">楷体</FONT><BR> <FONT face="黑体">黑体</FONT><BR> <FONT face="隶书">隶书</FONT><BR> <FONT face="幼圆">幼圆</FONT> <P>以下是常用英文字体:</P> <FONT face="Times New Roman"> Times New Roman </FONT><BR> <FONT face="Arial">Arial</FONT><BR> <FONT face="Arial Black">Arial Black</FONT><BR> <FONT face="Courtier New">Courtier New</FONT><BR> <FONT face="Comic Sans MS">Comic Sans MS</FONT><BR> <FONT face="Verdana">Verdana</FONT><BR> </DIV> </BODY> </HTML> 显示效果如下图所示: 注意:字体样式拼写需书写正确。6.B\I\U字体样式及物理元素与逻辑元素 主要包括: --<b> ... </b>文字加粗 --<strong> ... </strong> 文本加重语气 --<big> ... </big> 字体大一号 --<small> ... </small> 字体小号效果 --<em> ... </em>着重显示(emphasized) --<i> ... </i> 字体斜体(italic) --<sub> ... </sub> 定义下标,如H20 水 --<sup> ... </sup> 定义上标,如O2 氧气 --<u> ... </u> 下划线 --<s> ... </s> 删除文本 例如: <HTML> <HEAD><TITLE>物理字符样式效果示例</TITLE></HEAD> <BODY> <P><B>此处为粗体b显示文本</B></P> <P><STRONG>此为粗体strong显示</STRONG> <P><BIG>此处为大字big体文本</BIG></P> <P><SMALL>此处为小字small体文本</SMALL></P> <P><I>此处为斜体i文本</I></P> <P><EM>此为加重语气em斜体</EM></P> <P><TT>此处为等宽tt字体文本</TT></P> <P><U>此处为下划线u文本</U></P> <P><S>此处为使用<S>标记设置的删除线文本</S></P> <P><STRIKE>此处为使用<STRIKE>标记设置的删除线文本</STRIKE></P> <P>此处为上标示例:x<SUP>2</SUP> + y<SUP>2</SUP> = R<SUP>2</SUP></P> <P>此处为下标示例:H<SUB>2</SUB>SO<SUB>4</SUB></P> </BODY> </HTML> 显示效果如下图所示: 物理元素:加粗Bold,表示标记符本身说明了所修饰的效果。 逻辑元素:strong强调某段文字的消息,表示标记符说明所修饰效果的逻辑含义。 7.特色字符<>引号等 如果用户需要在网页中显示某些特色字符,例如<(小括号)、>(大括号)、”(引号)等,需要使用特殊字符来表示。 参考字符以&开始,以;结束。既可以使用数字代码,也可以使用名称代码。 例如:显示1<2 <p>1&lt;2</p> HTML特殊字符编码对照表 参考:http://www.jb51.net/onlineread/htmlchar.htm 三. HTML设置段落格式 1.分段标记符P 分段标记符用于将文档划分为段落,标记为<p></p>。 其中结束标记符通常可以省略。 2.换行标记符BR 在HTML中换行通常使用<br />或<br>。它是在不产生一个新段落情况下的换行。单标签元素 />关闭。 注意:在HTML源码中设置多个空格或换行,所有的连续空格或换行被显示为一个空格,浏览器会忽略源码排版信息,移除多余空格与换行;而如果使用<br />标签可以实现多个换行,&nbsp;实现多个空格。 3.标题样式Hn 在HTML中,用户可以通过Hn标记符来识别文档中的标题和副标题,其中n是数字1到6;H1表示最大的标题,H6为最小标题,使用标题样式时,必须使用结束标记符。 注意:使用标题时应按照其逻辑含义,而不是按照显示效果。换句话就是你不能为了产生大点的字体就使用它们,设置字体请使用其他标签或CSS替代。 4.添加水平线HR 它包括以下属性: --size:设置水平线的粗细 --width:设置水平线长度 --noshade:设置水平线以实线显示 --color:设置水平线颜色 例如: <HTML> <HEAD> <TITLE>水平线效果</TITLE> </HEAD> <BODY> 以下是默认水平线:<HR> 以下是粗为 5 像素的水平线:<HR size="5"> 以下是长度为100像素的水平线:<HR width="100"> 以下是长度为屏幕宽度 50% 的水平线:<HR width="50%"> 以下是粗为 5 像素的实心水平线:<HR size="5" noshade="noshade"> 以下是红色的水平线:<HR color="red"> </BODY> </HTML> 显示效果如下图所示: 5.align属性设置对齐方式 设置段落对齐方式,其值包括: --right:右对齐 --left:左对齐 --center:居中对齐 --justify:两端对齐 注意:align属性可以用于多种标记符,最典型为P、Hn、HR、table、div等。在讲述table、div布局时会详细介绍。 6.HTML列表 (1)有序列表(Ordered List) 在表的各项前显示数字或字母的缩排列表。 有序列表标记符<ol></ol>,列表项标记符<li></li>(结束可省略</li>)。 <ol> <li> List item 1 <li> List item 2 </ol> 注意:type属性设置样式,取值为1、A、a、I、i。 <HTML> <HEAD> <TITLE>有序列表示例</TITLE> </HEAD> <BODY> 一般的有序列表 <OL> <LI>列表项1 <LI>列表项2 <LI>列表项3 </OL> 用大写罗马字母表示的有序列表: <OL type="I"> <LI>列表项1 <LI>列表项2 <LI>列表项3 </OL> </BODY> </HTML> 显示效果如下图所示: (2)无序列表(Unordered List) 它是一种在表各项前显示特殊项目符号的缩排列表。 无序列表标记符<ul></ul>,列表项标记符<li></li>(结束可省略</li>)。 <ul> <li> List item 1 <li> List item 2 </ul> 注意:type属性设置样式,取值为disc、circle、square。在IE中,type属性的取值是区分大小写的,通常使用小写。 <HTML> <HEAD><TITLE>无序列表示例</TITLE></HEAD> <BODY> 默认无序列表: <UL><LI>列表项1<LI>列表项2<LI>列表项3</UL> 使用方块作为列表项标记的无序列表: <UL type="square"> <LI>列表项1<LI>列表项2<LI>列表项3 </UL> </BODY> </HTML> 显示效果如下图所示: (3)自定义列表 格式如下: <dl> <dt> </dt> <dd> </dd> <dt> </dt> <dd> </dd> </dl> 最后希望文章对你有所帮助,主要讲述了HTML相关的基础知识,如果文章中存在错误或不足之处,还请海涵~文章中的部分例子是我讲课的内容,部分是我读书时的HTML网页内容。 (By:Eastmount 2016-7-28 清晨5点 )
在Python网络爬虫中,通常是通过TXT纯文本方式存储,其实也是可以存储在数据库中的;同时在WAMP(Windows、Apache、MySQL、PHP或Python)开发网站中,也可以通过Python构建网页的,所以这篇文章主要讲述Python调用MySQL数据库相关编程知识。从以下几个方面进行讲解: 1.配置MySLQ 2.SQL语句基础知识 3.Python操作MySQL基础知识 4.Python调用MySQL示例 一. 配置MySQL 首先下载mysql-5.0.96-winx64,安装过程如下图所示。 1.安装MySQL 5.0 2.选择手动配置、服务类型、通用多功能型和安装路径 3.设置数据库访问量连接数为15、端口为3306(代码中设置URL用到)、编码方式为utf-8 4.设置默认超级root用户的用户名和密码,最后安装成功 二. SQL语句基础知识 安装MySQL 5.0成功后,进行数据库的简单操作。 1.运行MySQL输入默认用户密码123456 2.创建数据库test01和使用数据库(第二次调用直接use database) create database test01; 显示数据库中包含的数据库:show databases; 3.创建表student,其中学号为主键 create table student(username varchar(20),password varchar(20),stuid int primary key); 4.显示表结构,使用语句desc student 5.向学生表中插入数据并显示查询的数据 6.删除表:drop table student; 7.更新数据 update student set password=’000000’ where stuid=’1’; 8.删除数据 Delete from student where username=’eastmount; 此时MySQL操作数据库基本讲解结束,你同样可以实现数据库的增删改查、事务、存储过程等操作,建议安装可视化的软件来替代黑框,或使用Navicat for MySQL软件即可。代码如下:Enter password: ****** mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | test | | test01 | +--------------------+ 5 rows in set (0.00 sec) mysql> use test01; Database changed mysql> show tables; Empty set (0.00 sec) mysql> create table student(username varchar(20), -> password varchar(20), -> stuid int primary key); Query OK, 0 rows affected (0.33 sec) mysql> show tables; +------------------+ | Tables_in_test01 | +------------------+ | student | +------------------+ 1 row in set (0.00 sec) mysql> desc student; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | username | varchar(20) | YES | | NULL | | | password | varchar(20) | YES | | NULL | | | stuid | int(11) | NO | PRI | NULL | | +----------+-------------+------+-----+---------+-------+ 3 rows in set (0.03 sec) mysql> insert student(username, password, stuid) -> values('eastmount','123456',1) -> ; Query OK, 1 row affected (0.05 sec) mysql> select * from student; +-----------+----------+-------+ | username | password | stuid | +-----------+----------+-------+ | eastmount | 123456 | 1 | +-----------+----------+-------+ 1 row in set (0.00 sec) mysql> update student set password='000000' where stuid='1'; Query OK, 1 row affected (0.10 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from student; +-----------+----------+-------+ | username | password | stuid | +-----------+----------+-------+ | eastmount | 000000 | 1 | +-----------+----------+-------+ 1 row in set (0.00 sec) mysql> delete from student where username='eastmount'; Query OK, 1 row affected (0.08 sec) mysql> select * from student; Empty set (0.00 sec) mysql> 三. Python调用MySQL基础知识 通常的安装方法是使用:pip install mysql 安装Python的MySQL库,但是总会报错。常见错误如: Microsoft Visual C++ 9.0 is required (Unable to find vcvarsall.bat) mysql.c(42) : fatal error C1083: Cannot open include file: 'config-win.h': No such file or directory 这些可能是驱动等问题。 正确安装方法: 建议下载一个MySQL-python-1.2.3.win-amd64-py2.7.exe文件进行安装。 官网地址:https://pypi.python.org/pypi/MySQL-python/ 下载地址:http://download.csdn.net/detail/eastmount/9598651 下面我们要详细了解Python数据库API。从Python中访问数据库需要接口程序,接口程序是一个Python模块,它提供数据库客户端库(通常是C语言写成的)的接口供你访问。注意:Python接口程序都一定要遵守Python DB-API规范。 DB-API是一个规范。它定义了一系列必须的对象和数据库存取方式,以便为各种各样的底层数据库系统和多种多样的数据库接口程序提供一致的访问接口。DB-API为不同的数据库提供了一致的访问接口,在不同的数据库之间移植代码成为一件轻松的事情。 下面简单介绍DB-API的使用方法。 1.模块属性 DB-API规范里的以下特性和属性必须提供。一个DB-API兼容模块定义如下所示:apilevel:模块兼容的DB-API版本号 threadsafety:线程安全级别 paramstyle:支持sql语句参数风格 connect():连接数据库 Python调用MsSQL需要导入MySQLdb库,如下: import MySQLdb 2.connect()函数 其中主要使用的方法是connect对象。connect()方法生成一个connect对象,用于访问数据库,其参数如下: user:Username password:Password host:Hostname database:DatabaseName dsn:Data source name 注意并非所有的接口程序都严格按照这种格式,如MySQLdb。 import MySQLdb conn = MySQLdb.connect(host='localhost', db='test01', user='root', passwd='123456', port=3306, charset='utf8') connect()对象方法如下: lose():关闭数据库连接,或者关闭游标对象 commit():提交当前事务 rollback():取消当前事务 cursor():创建游标或类游标对象 errorhandler(cxn,errcls,errval):作为已给游标的句柄 注意,执行close()方法则上述的连接对象方法不能再使用,否则发生异常。commit()、rollback()、cursor()或许更对于支持事务的数据库更有意义。 数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完整地执行,要么完全地不执行。 一旦你完成了数据库连接,关闭了游标对象,然后在执行commit()提交你的操作,然后关闭连接。 3.游标对象 上面说了connect()方法用于提供连接数据库的接口,如果要对数据库操作那么还需要使用游标对象。游标对象的属性和方法: fetchone():可以看作fetch(取出) one(一个),也就是得到结果集的下一行(一行)。 fetchmany(size):可以看作fetch(取出)many(多个),这里的参数是界限,得到结果集的下几行(几行) fetchall():顾名思义,取得所有。 execute(sql):执行数据库操作,参数为sql语句。 close():不需要游标时尽可能的关闭 下面通过简单的示例进行讲解。 四. Python调用MySQL示例 在前面数据库中我们创建了数据库“test01”和表“student”,同时插入了数据。那么,怎样通过Python来显示呢? 1.查询所有数据库 首先,我们查看本地数据库中所包含的数据库名称,通过“show databases”语句。 import MySQLdb try: conn=MySQLdb.connect(host='localhost',user='root',passwd='123456',port=3306) cur=conn.cursor() res = cur.execute('show databases') print res for data in cur.fetchall(): print '%s' % data cur.close() conn.close() except MySQLdb.Error,e: print "Mysql Error %d: %s" % (e.args[0], e.args[1]) 其中通过链接数据库代码为:conn=MySQLdb.connect(host='localhost',user='root',passwd='123456',port=3306) 访问root超级用户,其密码为“123456”,端口为“3306”,其结果如下: 如果不知道本地数据库的名称,可以通过该方法,先查询数据库中包含哪些数据库,然后再连接该数据库进行相关的操作。 2.查询表 下面介绍查询表student中数据,代码如下,代码的具体含义是通过connect()连接数据库,通过conn.cursor()定义游标,然后调用游标的excute(sql)执行数据库操作,此处为查询操作,再通过fetchall()函数获取所有数据。 # coding:utf-8 import MySQLdb try: conn=MySQLdb.connect(host='localhost',user='root',passwd='123456',port=3306, db='test01', charset='utf8') cur=conn.cursor() res = cur.execute('select * from student') print u'表中包含',res,u'条数据\n' print u'数据如下:(姓名 密码 序号)' for data in cur.fetchall(): print '%s %s %s' % data cur.close() conn.close() except MySQLdb.Error,e: print "Mysql Error %d: %s" % (e.args[0], e.args[1]) 输出结果如图所示: 对应的MySQL中的结果是一致的,下图是对应的结果。 3.创建表 下面这段代码是创建一张教师表,主要是通过commit()提交数据。# coding:utf-8 import MySQLdb try: conn=MySQLdb.connect(host='localhost',user='root',passwd='123456',port=3306, db='test01', charset='utf8') cur=conn.cursor() #查看表 print u'插入前包含表:' cur.execute('show tables') for data in cur.fetchall(): print '%s' % data #插入数据 sql = '''create table teacher(id int not null primary key auto_increment, name char(30) not null, sex char(20) not null )''' cur.execute(sql) #查看表 print u'\n插入后包含表:' cur.execute('show tables') for data in cur.fetchall(): print '%s' % data cur.close() conn.commit() conn.close() except MySQLdb.Error,e: print "Mysql Error %d: %s" % (e.args[0], e.args[1]) 输出结果如下所示,插入教师表,包含字段:教师序号(id)、教师名称(name)、教师性别(sex)。 插入数据也可以通过execute(sql)方法实现,如: cur.execute("insert into student values( 'yxz', '111111', '10')") 但插入的新数据通常是通过变量进行赋值,而不是固定的,所以要对这条语句中的值做修改。我们可以做如下修改: # coding:utf-8 import MySQLdb try: conn=MySQLdb.connect(host='localhost',user='root',passwd='123456',port=3306, db='test01') cur=conn.cursor() #插入数据 sql = '''insert into student values(%s, %s, %s)''' cur.execute(sql, ('yxz','111111', '10')) #查看数据 print u'\n插入数据:' cur.execute('select * from student') for data in cur.fetchall(): print '%s %s %s' % data cur.close() conn.commit() conn.close() except MySQLdb.Error,e: print "Mysql Error %d: %s" % (e.args[0], e.args[1]) 输出结果如下所示: >>> 插入数据: esatmount 123456 1 yangxiuzhang 123456 2 xiaoy 123456 3 yxz 111111 10 >>> 同样,对数据库的增删改插都可以进行,请读者自行阅读。 推荐资料:python使用mysql数据库 - 虫师 后面我会结合Python爬虫讲述,如何将爬取的内容存储在数据库中,如我CSDN的博客,爬取博客标题、发布时间、阅读量和评论数。 MySQL数据库中结果如下图所示: 最后希望文章对你有所帮助,如果文章中存在不足或错误的地方,还请海涵~还是那句话,挺享受现在的老师生活,不论科研、项目,还是教学,很充实,加油! 但行好事,莫问前程。 待随满天李桃,再追学友趣事。 (By:Eastmount 2016-08-10 晚上10点 )
这篇文章主要记录MySQL中遇到的几个基础问题,希望文章对你有所帮助!包括: 1.日期类型的判断 2.decode函数的替代方法 3.查询语句中添加一个排序的序号 4.子函数查询select a.*1.日期类型判断 日期类型主要是:DATE(显示格式:YYYY-MM-DD) DATETTIME(显示格式:YYYY-MM-DD HH:MM:SS)假设存在学生表Student,如下图所示: (1)如何判断日期的年月日判断日:date_format(birthday,'%Y-%m-%d')='2016-08-23' 判断月:date_format(birthday,'%Y-%m')='2016-08' 判断年:date_format(birthday,'%Y')='2016' 对应的SQL语句如下: select * from student where date_format(birthday,'%Y-%m-%d')='2016-08-23'; 王二 111111 1 2016-08-23 21:05:46.000000 94 85 select * from student where date_format(birthday,'%Y-%m')='2016-08'; 王二 111111 1 2016-08-23 21:05:46.000000 94 85 杨三 123456 3 2016-08-17 21:06:28.000000 89 75 刘五 000000 4 2016-08-18 21:07:02.000000 61 92 select * from student where date_format(birthday,'%Y')='2016'; 王二 111111 1 2016-08-23 21:05:46.000000 94 85 李四 123456 2 2016-07-23 21:06:15.000000 76 87 杨三 123456 3 2016-08-17 21:06:28.000000 89 75 刘五 000000 4 2016-08-18 21:07:02.000000 61 92 (2)如何判断时间范围,如在2016-08-17到2016-08-20之间的数据通常会使用between and来进行比较select * from student where date_format(birthday,'%Y-%m-%d') between '2016-08-17' and '2016-08-20'; 如下图所示: (3)获取日期的年月日,使用YEAR()、month()、day()函数 select username, stuid, YEAR(birthday), month(birthday), day(birthday) from student; 输出如下图所示: 2.decode取代函数 在Oracle中存在decode(b,0,0,a/b)函数,用于防止分母为0,如果b为0返回0,否则返回a/b。但是MySQL中没有该函数,需要通过CASE WHEN进行替换。替代代码:case b when 0 then 0 else a/b end具体含义:当b为0时返回0,否则返回a/b并结束 假设存在一个黄六,英语成绩为0,需要统计"数学/英语"的结果: select username, stuid, math, english, math/english from student; 此时黄六输出NULL空值,如下图所示: 而使用了case判断后:select username, stuid, math, english, case english when 0 then 0 else math/english end as cf from student; 输出如下图所示,同时可以输出自定义的值,如'分母为0'等。 3.添加排序序号 通常MySQL排序使用order by(从小到大),然后增加desc是从大到小排序。 select username, stuid, math, english from student order by math; select username, stuid, math, english from student order by math desc; 输出如下图所示: 但是如果需要增加一个序号,自定义的排名呢? 假设前段显示需要序号,通常是通过Java设置一个Num来加1显示,但是数据库如何实现呢?通过:select @rownum:=@rownum+1 as num, name from table, (select @rownum:=0) temp; 输出如下图所示,注意这是自定义一列,而不是表中某个字段属性。select @rownum:=@rownum+1 as num, username, stuid, math, english from student,(select @rownum:=0) B order by math; 另一种方法:set @i:= 0; select @i:= @i + 1 as `order`, a.* from a order by col desc; 同时,你可能会遇到一个问题,如下面的SQL语句: select @rownum:=@rownum+1 as num, A.UnitDepName, sum(CostSum), A.UnitArea, (case A.UnitArea when 0 then 0 else sum(CostSum)/(A.UnitArea) end) as avCostSum from all_unitdepinfo A, gc_nhfxxdwzxfhzmon T, (select @rownum:=0) B where (A.UnitCode=T.UnitCode and A.UnitDepCode=T.UnitDepCode) and (A.UnitCode='GC001') group by A.UnitDepCode order by sum(CostSum)/sum(A.UnitArea) desc; 它的功能是统计各个学院能耗,并且排名、添加相应的序号,但输出的结果如下: 存在的问题是自定义序号应该是从1到n,而它序列弄错了。怎么解决呢? 采用子查询进行修改,即:select @rownum:=@rownum+1 as num, D.* from (....) D,(select @rownum:=0) B;select @rownum:=@rownum+1 as num, D.* from (select A.UnitDepName, sum(CostSum), sum(A.UnitArea), (case A.UnitArea when 0 then 0 else sum(CostSum)/sum(A.UnitArea) end) as avCostSum from all_unitdepinfo A, gc_nhfxxdwzxfhzmon T where (A.UnitCode=T.UnitCode and A.UnitDepCode=T.UnitDepCode) and (A.UnitCode='GC001') group by A.UnitDepCode order by sum(CostSum)/sum(A.UnitArea) desc) D, (select @rownum:=0) B; 输出结果如下图所示,即可: 注意:只有当你在实际操作数据库过程中遇到了该问题,才知道这篇文章的好处! 4.子函数select a.*查询 如果需要连接两个查询,并通过子查询实现,如果每个值能一一对应上,建议使用select a.* SELECT a.*, b.* FROM (SELECT SUM(DOMESTIC_TRAIN) + SUM(OVERSEA_TRAIN_TOTAL) AS zj, SUM(DEGREE_PHD) AS qzgdbsx, SUM(DOMESTIC_TRAIN) AS jnjxrcs, SUM(OVERSEA_TRAIN_TOTAL) AS jwjxrcs FROM TRAIN_INTERFLOW where YEAR_START=to_char(sysdate,'yyyy')-2 ) a, (SELECT SUM(PARTICIPANT_NUMBER) AS cyjglxkrcs FROM EDU_REVOLUTION where YEAR_START=to_char(sysdate,'yyyy')-2 ) b; 通常用于统计总数,不同总数进行总和,返回一条数据的情况。其中表对应各学院的信息,但希望你能学习这种SQL语句的格式。 输出如下图所示: 字符串拼接输出用法如下,输出"1-8"时间段:concat(TimeStart,'-',TimeEnd) 最后希望文章对你有所帮组,主要是记录实际遇到的几个问题,如果文章中存在不足之处,还请海涵~数据库SQL语句确实只有当你真正遇到相关问题时,才会觉得相关解决方法有用。 同时后面的文章会讲解如何使用Navicat for MySQL讲解设置主键、外键、递增序列等。 (By:Eastmount 2016-08-27)
这篇文章主要是讲述如何通过LDA处理文本内容TXT,并计算其文档主题分布,主要是核心代码为主。其中LDA入门知识介绍参考这篇文章,包括安装及用法: [python] LDA处理文档主题分布代码入门笔记 1.输入输出 输入是test.txt文件,它是使用Jieba分词之后的文本内容,通常每行代表一篇文档。 该文本内容原自博客:文本分析之TFIDF/LDA/Word2vec实践 ,推荐大家去阅读。新春 备 年货 , 新年 联欢晚会 新春 节目单 , 春节 联欢晚会 红火 大盘 下跌 股市 散户 下跌 股市 赚钱 金猴 新春 红火 新年 新车 新年 年货 新春 股市 反弹 下跌 股市 散户 赚钱 新年 , 看 春节 联欢晚会 大盘 下跌 散户 输出则是这十篇文档的主题分布,Shape(10L, 2L)表示10篇文档,2个主题。 具体结果如下所示:shape: (10L, 2L) doc: 0 topic: 0 doc: 1 topic: 0 doc: 2 topic: 1 doc: 3 topic: 1 doc: 4 topic: 0 doc: 5 topic: 0 doc: 6 topic: 1 doc: 7 topic: 1 doc: 8 topic: 0 doc: 9 topic: 1 同时调用 matplotlib.pyplot 输出了对应的文档主题分布图,可以看到主题Doc0、Doc1、Doc8分布于Topic0,它们主要描述主题新春;而Doc2、Doc3、Doc9分布于Topic1,主要描述股市。 其过程中也会输出描述LDA运行的信息,如下图所示: 2.核心代码 其中核心代码如下图所示,包括读取文本、LDA运行、输出绘图等操作。 # coding=utf-8 import os import sys import numpy as np import matplotlib import scipy import matplotlib.pyplot as plt from sklearn import feature_extraction from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import HashingVectorizer if __name__ == "__main__": #存储读取语料 一行预料为一个文档 corpus = [] for line in open('test.txt', 'r').readlines(): #print line corpus.append(line.strip()) #print corpus #将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频 vectorizer = CountVectorizer() print vectorizer X = vectorizer.fit_transform(corpus) analyze = vectorizer.build_analyzer() weight = X.toarray() print len(weight) print (weight[:5, :5]) #LDA算法 print 'LDA:' import numpy as np import lda import lda.datasets model = lda.LDA(n_topics=2, n_iter=500, random_state=1) model.fit(np.asarray(weight)) # model.fit_transform(X) is also available topic_word = model.topic_word_ # model.components_ also works #文档-主题(Document-Topic)分布 doc_topic = model.doc_topic_ print("type(doc_topic): {}".format(type(doc_topic))) print("shape: {}".format(doc_topic.shape)) #输出前10篇文章最可能的Topic label = [] for n in range(10): topic_most_pr = doc_topic[n].argmax() label.append(topic_most_pr) print("doc: {} topic: {}".format(n, topic_most_pr)) #计算文档主题分布图 import matplotlib.pyplot as plt f, ax= plt.subplots(6, 1, figsize=(8, 8), sharex=True) for i, k in enumerate([0, 1, 2, 3, 8, 9]): ax[i].stem(doc_topic[k,:], linefmt='r-', markerfmt='ro', basefmt='w-') ax[i].set_xlim(-1, 2) #x坐标下标 ax[i].set_ylim(0, 1.2) #y坐标下标 ax[i].set_ylabel("Prob") ax[i].set_title("Document {}".format(k)) ax[5].set_xlabel("Topic") plt.tight_layout() plt.show() 同时如果希望查询每个主题对应的问题词权重分布情况如下: import matplotlib.pyplot as plt f, ax= plt.subplots(2, 1, figsize=(6, 6), sharex=True) for i, k in enumerate([0, 1]): #两个主题 ax[i].stem(topic_word[k,:], linefmt='b-', markerfmt='bo', basefmt='w-') ax[i].set_xlim(-2,20) ax[i].set_ylim(0, 1) ax[i].set_ylabel("Prob") ax[i].set_title("topic {}".format(k)) ax[1].set_xlabel("word") plt.tight_layout() plt.show() 运行结果如下图所示:共2个主题Topics,15个核心词汇。 绘图推荐文章:http://blog.csdn.net/pipisorry/article/details/37742423 PS:讲到这里,整个完整的LDA算法就算结束了,你可以通过上面的代码进行LDA主题分布的计算,下面是一些问题。 3.TFIDF计算及词频TF计算 特征计算方法参考:Feature Extraction - scikit-learn #计算TFIDF corpus = [] #读取预料 一行预料为一个文档 for line in open('test.txt', 'r').readlines(): #print line corpus.append(line.strip()) #print corpus #将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频 vectorizer = CountVectorizer() #该类会统计每个词语的tf-idf权值 transformer = TfidfTransformer() #第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵 tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus)) #获取词袋模型中的所有词语 word = vectorizer.get_feature_names() #将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重 weight = tfidf.toarray() #打印特征向量文本内容 print 'Features length: ' + str(len(word)) for j in range(len(word)): print word[j] #打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重 for i in range(len(weight)): for j in range(len(word)): print weight[i][j], print '\n' 输出如下图所示,共统计处特征词15个,对应TF-IDF矩阵,共10行数据对应txt文件中的10个文档,每个文档15维数据,存储TF-IDF权重,这就可以通过10*15的矩阵表示整个文档权重信息。Features length: 15 下跌 反弹 大盘 年货 散户 新年 新春 新车 春节 红火 联欢晚会 股市 节目单 赚钱 金猴 0.0 0.0 0.0 0.579725686076 0.0 0.450929562568 0.450929562568 0.0 0.0 0.0 0.507191470855 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.356735384792 0.0 0.458627428458 0.458627428458 0.401244805261 0.0 0.539503693426 0.0 0.0 0.450929562568 0.0 0.579725686076 0.0 0.507191470855 0.0 0.0 0.0 0.0 0.0 0.0 0.450929562568 0.0 0.0 0.0 0.523221265036 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.523221265036 0.0 0.672665604612 0.0 0.0 0.0 0.0 0.0 0.0 0.410305398084 0.410305398084 0.0 0.0 0.52749830162 0.0 0.0 0.0 0.0 0.620519542315 0.0 0.0 0.0 0.52749830162 0.0 0.410305398084 0.410305398084 0.620519542315 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.482964462575 0.730404446714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.482964462575 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.568243852685 0.0 0.0 0.0 0.0 0.0 0.0 0.505209504985 0.0 0.649509260872 0.0 0.0 0.0 0.0 0.0 0.0 0.505209504985 0.0 0.0 0.649509260872 0.0 0.568243852685 0.0 0.0 0.0 0.0 0.505209504985 0.0 0.649509260872 0.0 0.568243852685 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 但是在将TF-IDF用于LDA算法model.fit(np.asarray(weight))时,总是报错如下: TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe' 所以后来LDA我采用的是统计词频的方法进行的,该段代码如下: #存储读取语料 一行预料为一个文档 corpus = [] for line in open('test.txt', 'r').readlines(): #print line corpus.append(line.strip()) #print corpus #将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频 vectorizer = CountVectorizer() #fit_transform是将文本转为词频矩阵 X = vectorizer.fit_transform(corpus) #获取词袋模型中的所有词语 word = vectorizer.get_feature_names() analyze = vectorizer.build_analyzer() weight = X.toarray() #打印特征向量文本内容 print 'Features length: ' + str(len(word)) for j in range(len(word)): print word[j], #打印每类文本词频矩阵 print 'TF Weight: ' for i in range(len(weight)): for j in range(len(word)): print weight[i][j], print '\n' print len(weight) print (weight[:5, :5]) 输出如下所示:Features length: 15 下跌 反弹 大盘 年货 散户 新年 新春 新车 春节 红火 联欢晚会 股市 节目单 赚钱 金猴 TF Weight: 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 0 1 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 10 [[0 0 0 1 0] [0 0 0 0 0] [1 0 1 0 1] [1 0 0 0 0] [0 0 0 0 0]] 得到weight权重后,然后调用对应的算法即可执行不用的应用,如: import lda model = lda.LDA(n_topics=20, n_iter=500, random_state=1) model.fit(np.asarray(weight)) from sklearn.cluster import KMeans clf = KMeans(n_clusters=4) #景区 动物 人物 国家 s = clf.fit(weight) 4.百度互动主题分布例子 输入数据主要是前面讲述过的爬取百度百科、互动百科的景区、动物、人物、国家四类信息,具体如下所示: 输出如下所示,共12行数据,其中doc0~doc2主题分布为topic1,其主题表示景区;doc3~doc5主题分布为topic3,其主题表示动物;doc6~doc8主题分布为topic0,其主题表示人物;doc9~doc11主题分布为topic2,其主题表示国家。shape: (12L, 4L) doc: 0 topic: 1 doc: 1 topic: 1 doc: 2 topic: 1 doc: 3 topic: 3 doc: 4 topic: 3 doc: 5 topic: 3 doc: 6 topic: 0 doc: 7 topic: 0 doc: 8 topic: 0 doc: 9 topic: 2 doc: 10 topic: 2 doc: 11 topic: 2 讲到此处你也应该理解了LDA的基本用法和适用场景,你可以通过它进行新闻主题分布,同时再进行引文推荐、聚类算法等操作。 总之,希望这篇基础性的文章对你有所帮助吧!还是那句话: 虽然我写这类文章看起来很简单,尤其对于机器学习的大牛来说,感觉没什么实质内容;但是如果你刚接触这类知识,还是非常头疼的,想找到一个可运行的算法很困难。 这也是为什么总感觉以前学习了一些原理或理论的东西,而实际应用不是很明白,这种感觉就像学游泳,在岸上看别人感觉什么都会了,但想要学会还是得下水,一步一步来,而我写的这类基础文章就相当于带你下水吧!后面你才能做些自己喜欢的算法和研究。 最近真的很忙,同时我认识了一位很优秀的女生,总算迈出了人生最重要的一步,就是真正的勇敢的出去接触些异性朋友,这感觉非常不错的。同时学校工作那边仍然在等消息,真心想回家当一名软件相关的教师啊~ 最后附上最近朋友圈的一条信息: 哎!感叹下时光吧,仅以此诗纪念这三年写博客的坚持和北理最后的四个月: 但行好事,莫问前程。 待随满天李桃,再追学友趣事。 (By:Eastmount 2016-03-15 深夜3点 http://blog.csdn.net/eastmount/ )
以前只知道LDA是个好东西,但自己并没有真正去使用过。同时,关于它的文章也非常之多,推荐大家阅读书籍《LDA漫游指南》,最近自己在学习文档主题分布和实体对齐中也尝试使用LDA进行简单的实验。这篇文章主要是讲述Python下LDA的基础用法,希望对大家有所帮助。如果文章中有错误或不足之处,还请海涵~ 一. 下载安装 LDA推荐下载地址包括:其中前三个比较常用。 gensim下载地址:https://radimrehurek.com/gensim/models/ldamodel.html pip install lda安装地址:https://github.com/ariddell/lda scikit-learn官网文档:LatentDirichletAllocation 其中sklearn的代码例子可参考下面这篇: Topic extraction with NMF and Latent Dirichlet Allocation 其部分输出如下所示,包括各个主体Topic包含的主题词: Loading dataset... Fitting LDA models with tf features, n_samples=2000 and n_features=1000... done in 0.733s. Topics in LDA model: Topic #0: 000 war list people sure civil lot wonder say religion america accepted punishment bobby add liberty person kill concept wrong Topic #1: just reliable gods consider required didn war makes little seen faith default various civil motto sense currency knowledge belief god Topic #2: god omnipotence power mean rules omnipotent deletion policy non nature suppose definition given able goal nation add place powerful leaders .... 下面这三个也不错,大家有时间的可以见到看看: https://github.com/arongdari/python-topic-model https://github.com/shuyo/iir/tree/master/lda https://github.com/a55509432/python-LDA 其中第三个作者a55509432的我也尝试用过,模型输出文件为: model_parameter.dat 保存模型训练时选择的参数 wordidmap.dat 保存词与id的对应关系,主要用作topN时查询 model_twords.dat 输出每个类高频词topN个 model_tassgin.dat 输出文章中每个词分派的结果,文本格式为词id:类id model_theta.dat 输出文章与类的分布概率,文本一行表示一篇文章,概率1 概率2..表示文章属于类的概率 model_phi.dat 输出词与类的分布概率,是一个K*M的矩阵,K为设置分类的个数,M为所有文章的词的总数 但是短文本信息还行,但使用大量文本内容时,输出文章与类分布概率几乎每行数据存在大量相同的,可能代码还存在BUG。 下面是介绍使用pip install lda安装过程及代码应用:pip install lda 参考:[python] 安装numpy+scipy+matlotlib+scikit-learn及问题解决 二. 官方文档 这部分内容主要参考下面几个链接,强推大家去阅读与学习: 官网文档:https://github.com/ariddell/lda lda: Topic modeling with latent Dirichlet Allocation Getting started with Latent Dirichlet Allocation in Python - sandbox [翻译] 在Python中使用LDA处理文本 - letiantian 文本分析之TFIDF/LDA/Word2vec实践 - vs412237401 1.载入数据 import numpy as np import lda import lda.datasets # document-term matrix X = lda.datasets.load_reuters() print("type(X): {}".format(type(X))) print("shape: {}\n".format(X.shape)) print(X[:5, :5]) # the vocab vocab = lda.datasets.load_reuters_vocab() print("type(vocab): {}".format(type(vocab))) print("len(vocab): {}\n".format(len(vocab))) print(vocab[:5]) # titles for each story titles = lda.datasets.load_reuters_titles() print("type(titles): {}".format(type(titles))) print("len(titles): {}\n".format(len(titles))) print(titles[:5]) 载入LDA包数据集后,输出如下所示:X矩阵为395*4258,共395个文档,4258个单词,主要用于计算每行文档单词出现的次数(词频),然后输出X[5,5]矩阵; vocab为具体的单词,共4258个,它对应X的一行数据,其中输出的前5个单词,X中第0列对应church,其值为词频; titles为载入的文章标题,共395篇文章,同时输出0~4篇文章标题如下。type(X): <type 'numpy.ndarray'> shape: (395L, 4258L) [[ 1 0 1 0 0] [ 7 0 2 0 0] [ 0 0 0 1 10] [ 6 0 1 0 0] [ 0 0 0 2 14]] type(vocab): <type 'tuple'> len(vocab): 4258 ('church', 'pope', 'years', 'people', 'mother') type(titles): <type 'tuple'> len(titles): 395 ('0 UK: Prince Charles spearheads British royal revolution. LONDON 1996-08-20', '1 GERMANY: Historic Dresden church rising from WW2 ashes. DRESDEN, Germany 1996-08-21', "2 INDIA: Mother Teresa's condition said still unstable. CALCUTTA 1996-08-23", '3 UK: Palace warns British weekly over Charles pictures. LONDON 1996-08-25', '4 INDIA: Mother Teresa, slightly stronger, blesses nuns. CALCUTTA 1996-08-25') From the above we can see that there are 395 news items (documents) and a vocabulary of size 4258. The document-term matrix, X, has a count of the number of occurences of each of the 4258 vocabulary words for each of the 395 documents. 下面是测试文档编号为0,单词编号为3117的数据,X[0,3117]:# X[0,3117] is the number of times that word 3117 occurs in document 0 doc_id = 0 word_id = 3117 print("doc id: {} word id: {}".format(doc_id, word_id)) print("-- count: {}".format(X[doc_id, word_id])) print("-- word : {}".format(vocab[word_id])) print("-- doc : {}".format(titles[doc_id])) '''输出 doc id: 0 word id: 3117 -- count: 2 -- word : heir-to-the-throne -- doc : 0 UK: Prince Charles spearheads British royal revolution. LONDON 1996-08-20 ''' 2.训练模型 其中设置20个主题,500次迭代model = lda.LDA(n_topics=20, n_iter=500, random_state=1) model.fit(X) # model.fit_transform(X) is also available 3.主题-单词(topic-word)分布 代码如下所示,计算'church', 'pope', 'years'这三个单词在各个主题(n_topocs=20,共20个主题)中的比重,同时输出前5个主题的比重和,其值均为1。topic_word = model.topic_word_ print("type(topic_word): {}".format(type(topic_word))) print("shape: {}".format(topic_word.shape)) print(vocab[:3]) print(topic_word[:, :3]) for n in range(5): sum_pr = sum(topic_word[n,:]) print("topic: {} sum: {}".format(n, sum_pr)) 输出结果如下:type(topic_word): <type 'numpy.ndarray'> shape: (20L, 4258L) ('church', 'pope', 'years') [[ 2.72436509e-06 2.72436509e-06 2.72708945e-03] [ 2.29518860e-02 1.08771556e-06 7.83263973e-03] [ 3.97404221e-03 4.96135108e-06 2.98177200e-03] [ 3.27374625e-03 2.72585033e-06 2.72585033e-06] [ 8.26262882e-03 8.56893407e-02 1.61980569e-06] [ 1.30107788e-02 2.95632328e-06 2.95632328e-06] [ 2.80145003e-06 2.80145003e-06 2.80145003e-06] [ 2.42858077e-02 4.66944966e-06 4.66944966e-06] [ 6.84655429e-03 1.90129250e-06 6.84655429e-03] [ 3.48361655e-06 3.48361655e-06 3.48361655e-06] [ 2.98781661e-03 3.31611166e-06 3.31611166e-06] [ 4.27062069e-06 4.27062069e-06 4.27062069e-06] [ 1.50994982e-02 1.64107142e-06 1.64107142e-06] [ 7.73480150e-07 7.73480150e-07 1.70946848e-02] [ 2.82280146e-06 2.82280146e-06 2.82280146e-06] [ 5.15309856e-06 5.15309856e-06 4.64294180e-03] [ 3.41695768e-06 3.41695768e-06 3.41695768e-06] [ 3.90980357e-02 1.70316633e-03 4.42279319e-03] [ 2.39373034e-06 2.39373034e-06 2.39373034e-06] [ 3.32493234e-06 3.32493234e-06 3.32493234e-06]] topic: 0 sum: 1.0 topic: 1 sum: 1.0 topic: 2 sum: 1.0 topic: 3 sum: 1.0 topic: 4 sum: 1.0 4.计算各主题Top-N个单词 下面这部分代码是计算每个主题中的前5个单词n = 5 for i, topic_dist in enumerate(topic_word): topic_words = np.array(vocab)[np.argsort(topic_dist)][:-(n+1):-1] print('*Topic {}\n- {}'.format(i, ' '.join(topic_words))) 输出如下所示:*Topic 0 - government british minister west group *Topic 1 - church first during people political *Topic 2 - elvis king wright fans presley *Topic 3 - yeltsin russian russia president kremlin *Topic 4 - pope vatican paul surgery pontiff *Topic 5 - family police miami versace cunanan *Topic 6 - south simpson born york white *Topic 7 - order church mother successor since *Topic 8 - charles prince diana royal queen *Topic 9 - film france french against actor *Topic 10 - germany german war nazi christian *Topic 11 - east prize peace timor quebec *Topic 12 - n't told life people church *Topic 13 - years world time year last *Topic 14 - mother teresa heart charity calcutta *Topic 15 - city salonika exhibition buddhist byzantine *Topic 16 - music first people tour including *Topic 17 - church catholic bernardin cardinal bishop *Topic 18 - harriman clinton u.s churchill paris *Topic 19 - century art million museum city 5.文档-主题(Document-Topic)分布 计算输入前10篇文章最可能的Topicdoc_topic = model.doc_topic_ print("type(doc_topic): {}".format(type(doc_topic))) print("shape: {}".format(doc_topic.shape)) for n in range(10): topic_most_pr = doc_topic[n].argmax() print("doc: {} topic: {}".format(n, topic_most_pr)) 输出如下所示:type(doc_topic): <type 'numpy.ndarray'> shape: (395L, 20L) doc: 0 topic: 8 doc: 1 topic: 1 doc: 2 topic: 14 doc: 3 topic: 8 doc: 4 topic: 14 doc: 5 topic: 14 doc: 6 topic: 14 doc: 7 topic: 14 doc: 8 topic: 14 doc: 9 topic: 8 6.两种作图分析 详见英文原文,包括计算各个主题中单词权重分布的情况:import matplotlib.pyplot as plt f, ax= plt.subplots(5, 1, figsize=(8, 6), sharex=True) for i, k in enumerate([0, 5, 9, 14, 19]): ax[i].stem(topic_word[k,:], linefmt='b-', markerfmt='bo', basefmt='w-') ax[i].set_xlim(-50,4350) ax[i].set_ylim(0, 0.08) ax[i].set_ylabel("Prob") ax[i].set_title("topic {}".format(k)) ax[4].set_xlabel("word") plt.tight_layout() plt.show() 输出如下图所示: 第二种作图是计算文档具体分布在那个主题,代码如下所示:import matplotlib.pyplot as plt f, ax= plt.subplots(5, 1, figsize=(8, 6), sharex=True) for i, k in enumerate([1, 3, 4, 8, 9]): ax[i].stem(doc_topic[k,:], linefmt='r-', markerfmt='ro', basefmt='w-') ax[i].set_xlim(-1, 21) ax[i].set_ylim(0, 1) ax[i].set_ylabel("Prob") ax[i].set_title("Document {}".format(k)) ax[4].set_xlabel("Topic") plt.tight_layout() plt.show() 输出结果如下图: 三. 总结 这篇文章主要是对Python下LDA用法的入门介绍,下一篇文章将结合具体的txt文本内容进行分词处理、文档主题分布计算等。其中也会涉及Python计算词频tf和tfidf的方法。 由于使用fit()总报错“TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'”,后使用sklearn中计算词频TF方法:http://scikit-learn.org/stable/modules/feature_extraction.html#text-feature-extraction 总之,希望文章对你有所帮助吧!尤其是刚刚接触机器学习、Sklearn、LDA的同学,毕竟我自己其实也只是一个门外汉,没有系统的学习过机器学习相关的内容,所以也非常理解那种不知道如何使用一种算法的过程,毕竟自己就是嘛,而当你熟练使用后才会觉得它非常简单,所以入门也是这篇文章的宗旨吧! 最后非常感谢上面提到的文章链接作者,感谢他们的分享。如果有不足之处,还请海涵~(By:Eastmount 2016-03-17 深夜3点半 http://blog.csdn.net/eastmount/ )
在进行自然语言处理、文本分类聚类、推荐系统、舆情分析等研究中,通常需要使用新浪微博的数据作为语料,这篇文章主要介绍如果使用Python和Selenium爬取自定义新浪微博语料。因为网上完整的语料比较少,而使用Selenium方法有点简单、速度也比较慢,但方法可行,同时能够输入验证码。希望文章对你有所帮助~爬取结果 首先可以爬取用户ID、用户名、微博数、粉丝数、关注数及微博信息。其中微博信息包括转发或原创、点赞数、转发数、评论数、发布时间、微博内容等等。如下图所示: 同时也可以爬取微博的众多用户的详细信息,包括基本信息、关注人ID列表和粉丝ID列表等等。如下图所示: 登录入口 新浪微博登录常用接口:http://login.sina.com.cn/ 对应主界面:http://weibo.com/但是个人建议采用手机端微博入口:http://login.weibo.cn/login/ 对应主界面:http://weibo.cn/ 其原因是手机端数据相对更轻量型,同时基本数据都齐全,可能缺少些个人基本信息,如"个人资料完成度"、"个人等级"等,同时粉丝ID和关注ID只能显示20页,但完全可以作为语料进行大部分的验证。通过比较下面两张图,分别是PC端和手机端,可以发现内容基本一致: 手机端下图所示,其中图片相对更小,同时内容更精简。 完整源码 下面代码主要分为三部分: 1.LoginWeibo(username, password) 登录微博 2.VisitPersonPage(user_id) 访问跟人网站,获取个人信息 3.获取微博内容,同时http://weibo.cn/guangxianliuyan?filter=0&page=1实现翻页# coding=utf-8 """ Created on 2016-02-22 @author: Eastmount 功能: 爬取新浪微博用户的信息 信息:用户ID 用户名 粉丝数 关注数 微博数 微博内容 网址:http://weibo.cn/ 数据量更小 相对http://weibo.com/ """ import time import re import os import sys import codecs import shutil import urllib from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #先调用无界面浏览器PhantomJS或Firefox #driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe") driver = webdriver.Firefox() wait = ui.WebDriverWait(driver,10) #全局变量 文件操作读写信息 inforead = codecs.open("SinaWeibo_List.txt", 'r', 'utf-8') infofile = codecs.open("SinaWeibo_Info.txt", 'a', 'utf-8') #******************************************************************************** # 第一步: 登陆weibo.cn 获取新浪微博的cookie # 该方法针对weibo.cn有效(明文形式传输数据) weibo.com见学弟设置POST和Header方法 # LoginWeibo(username, password) 参数用户名 密码 # 验证码暂停时间手动输入 #******************************************************************************** def LoginWeibo(username, password): try: #********************************************************************** # 直接访问driver.get("http://weibo.cn/5824697471")会跳转到登陆页面 用户id # # 用户名<input name="mobile" size="30" value="" type="text"></input> # 密码 "password_4903" 中数字会变动,故采用绝对路径方法,否则不能定位到元素 # # 勾选记住登录状态check默认是保留 故注释掉该代码 不保留Cookie 则'expiry'=None #********************************************************************** #输入用户名/密码登录 print u'准备登陆Weibo.cn网站...' driver.get("http://login.weibo.cn/login/") elem_user = driver.find_element_by_name("mobile") elem_user.send_keys(username) #用户名 elem_pwd = driver.find_element_by_xpath("/html/body/div[2]/form/div/input[2]") elem_pwd.send_keys(password) #密码 #elem_rem = driver.find_element_by_name("remember") #elem_rem.click() #记住登录状态 #重点: 暂停时间输入验证码 #pause(millisenconds) time.sleep(20) elem_sub = driver.find_element_by_name("submit") elem_sub.click() #点击登陆 time.sleep(2) #获取Coockie 推荐 http://www.cnblogs.com/fnng/p/3269450.html print driver.current_url print driver.get_cookies() #获得cookie信息 dict存储 print u'输出Cookie键值对信息:' for cookie in driver.get_cookies(): #print cookie for key in cookie: print key, cookie[key] #driver.get_cookies()类型list 仅包含一个元素cookie类型dict print u'登陆成功...' except Exception,e: print "Error: ",e finally: print u'End LoginWeibo!\n\n' #******************************************************************************** # 第二步: 访问个人页面http://weibo.cn/5824697471并获取信息 # VisitPersonPage() # 编码常见错误 UnicodeEncodeError: 'ascii' codec can't encode characters #******************************************************************************** def VisitPersonPage(user_id): try: global infofile print u'准备访问个人网站.....' #原创内容 http://weibo.cn/guangxianliuyan?filter=1&page=2 driver.get("http://weibo.cn/" + user_id) #************************************************************************** # No.1 直接获取 用户昵称 微博数 关注数 粉丝数 # str_name.text是unicode编码类型 #************************************************************************** #用户id print u'个人详细信息' print '**********************************************' print u'用户id: ' + user_id #昵称 str_name = driver.find_element_by_xpath("//div[@class='ut']") str_t = str_name.text.split(" ") num_name = str_t[0] #空格分隔 获取第一个值 "Eastmount 详细资料 设置 新手区" print u'昵称: ' + num_name #微博数 除个人主页 它默认直接显示微博数 无超链接 #Error: 'unicode' object is not callable #一般是把字符串当做函数使用了 str定义成字符串 而str()函数再次使用时报错 str_wb = driver.find_element_by_xpath("//div[@class='tip2']") pattern = r"\d+\.?\d*" #正则提取"微博[0]" 但r"(\[.*?\])"总含[] guid = re.findall(pattern, str_wb.text, re.S|re.M) print str_wb.text #微博[294] 关注[351] 粉丝[294] 分组[1] @他的 for value in guid: num_wb = int(value) break print u'微博数: ' + str(num_wb) #关注数 str_gz = driver.find_element_by_xpath("//div[@class='tip2']/a[1]") guid = re.findall(pattern, str_gz.text, re.M) num_gz = int(guid[0]) print u'关注数: ' + str(num_gz) #粉丝数 str_fs = driver.find_element_by_xpath("//div[@class='tip2']/a[2]") guid = re.findall(pattern, str_fs.text, re.M) num_fs = int(guid[0]) print u'粉丝数: ' + str(num_fs) #*************************************************************************** # No.2 文件操作写入信息 #*************************************************************************** infofile.write('=====================================================================\r\n') infofile.write(u'用户: ' + user_id + '\r\n') infofile.write(u'昵称: ' + num_name + '\r\n') infofile.write(u'微博数: ' + str(num_wb) + '\r\n') infofile.write(u'关注数: ' + str(num_gz) + '\r\n') infofile.write(u'粉丝数: ' + str(num_fs) + '\r\n') infofile.write(u'微博内容: ' + '\r\n\r\n') #*************************************************************************** # No.3 获取微博内容 # http://weibo.cn/guangxianliuyan?filter=0&page=1 # 其中filter=0表示全部 =1表示原创 #*************************************************************************** print '\n' print u'获取微博内容信息' num = 1 while num <= 5: url_wb = "http://weibo.cn/" + user_id + "?filter=0&page=" + str(num) print url_wb driver.get(url_wb) #info = driver.find_element_by_xpath("//div[@id='M_DiKNB0gSk']/") info = driver.find_elements_by_xpath("//div[@class='c']") for value in info: print value.text info = value.text #跳过最后一行数据为class=c #Error: 'NoneType' object has no attribute 'groups' if u'设置:皮肤.图片' not in info: if info.startswith(u'转发'): print u'转发微博' infofile.write(u'转发微博\r\n') else: print u'原创微博' infofile.write(u'原创微博\r\n') #获取最后一个点赞数 因为转发是后有个点赞数 str1 = info.split(u" 赞")[-1] if str1: val1 = re.match(r'\[(.*?)\]', str1).groups()[0] print u'点赞数: ' + val1 infofile.write(u'点赞数: ' + str(val1) + '\r\n') str2 = info.split(u" 转发")[-1] if str2: val2 = re.match(r'\[(.*?)\]', str2).groups()[0] print u'转发数: ' + val2 infofile.write(u'转发数: ' + str(val2) + '\r\n') str3 = info.split(u" 评论")[-1] if str3: val3 = re.match(r'\[(.*?)\]', str3).groups()[0] print u'评论数: ' + val3 infofile.write(u'评论数: ' + str(val3) + '\r\n') str4 = info.split(u" 收藏 ")[-1] flag = str4.find(u"来自") print u'时间: ' + str4[:flag] infofile.write(u'时间: ' + str4[:flag] + '\r\n') print u'微博内容:' print info[:info.rindex(u" 赞")] #后去最后一个赞位置 infofile.write(info[:info.rindex(u" 赞")] + '\r\n') infofile.write('\r\n') print '\n' else: print u'跳过', info, '\n' break else: print u'next page...\n' infofile.write('\r\n\r\n') num += 1 print '\n\n' print '**********************************************' except Exception,e: print "Error: ",e finally: print u'VisitPersonPage!\n\n' print '**********************************************\n' #******************************************************************************* # 程序入口 预先调用 #******************************************************************************* if __name__ == '__main__': #定义变量 username = '1520161***' #输入你的用户名 password = '**********' #输入你的密码 user_id = 'guangxianliuyan' #用户id url+id访问个人 #操作函数 LoginWeibo(username, password) #登陆微博 #driver.add_cookie({'name':'name', 'value':'_T_WM'}) #driver.add_cookie({'name':'value', 'value':'c86fbdcd26505c256a1504b9273df8ba'}) #注意 #因为sina微博增加了验证码,但是你用Firefox登陆一次输入验证码,再调用该程序即可,因为Cookies已经保证 #会直接跳转到明星微博那部分,即: http://weibo.cn/guangxianliuyan #在if __name__ == '__main__':引用全局变量不需要定义 global inforead 省略即可 print 'Read file:' user_id = inforead.readline() while user_id!="": user_id = user_id.rstrip('\r\n') VisitPersonPage(user_id) #访问个人页面 user_id = inforead.readline() #break infofile.close() inforead.close() PS:发现CSDN编辑器的BUG,只要包含( ) 如:r'\[(.*?)\]'就会自动换行 (⊙o⊙)登录页面 首先,为什么需要登录呢? 因为新浪微博很多数据如果不登录是不能获取或访问的,如微博的粉丝列表、个人详细信息、微博下一页等等,当你点击这些超链接时就会自动跳转到登录界面,这是开发者对其进行的保护措施。同时,各个公司都会提供API接口让开发者进行操作,但此处我是使用Selenium模拟浏览器操作进行爬取的。 其中登录如上图所示,函数LoginWeibo(username, password) 实现,它会自动打开浏览器并输入用户名和密码。在登录过程中由于会涉及到验证码,所以我采用暂停20秒,当用户手动输入验证码并且时间到后会自动点击按钮登录。核心代码如下:driver.get("http://login.weibo.cn/login/") elem_user = driver.find_element_by_name("mobile") elem_user.send_keys(username) #用户名 elem_pwd = driver.find_element_by_xpath("/html/body/div[2]/form/div/input[2]") elem_pwd.send_keys(password) #密码 elem_sub = driver.find_element_by_name("submit") elem_sub.click() #点击登陆 如果你登陆过程中Python报错:WebDriverException: Message: "Can't load the profile. Profile Dir: 猜测是Firefox版本问题,升级后出现的该问题,建议下载相对较老的版本,总体感觉只要Selenium、Python、Firefox版本一致就不会报错,可从下面链接中安装该版本Firefox。 下载地址:http://download.csdn.net/detail/mengh2016/7752097 那么,登录成功后,为什么就能访问或跳转到不同的页面呢? 因为登录成功后会保存Cookies或Session信息,此时用户就可以任意跳转访问了,否则会重新跳转会登录界面。这里使用Selenium的driver.get(url)实现跳转。获取个人信息首先很多网站设计都是 URL+用户ID 访问个人网站,如柳岩:http://weibo.cn/guangxianliuyan故定义一个TXT文件列表包含,所有用户ID信息,依次通过读取文件爬取其微博信息:user_id = inforead.readline() while user_id!="": user_id = user_id.rstrip('\r\n') VisitPersonPage(user_id) #访问个人页面 user_id = inforead.readline()其中用户ID列表在SinaWeibo_List.txt 中,如下所示:(明星)guangxianliuyan zhangjiani 1862829871 zmqd houzimi 3125046087 gezhaoen 1877716733 ailleenmmm linshenblog superleoisme 2638613703 duiersky ws95 wuwei1003673996 wuxin 1413971423 xiena yangxinyu888 zhangyangguoer0418 liuyifeiofficial 通过分析HTML源码,获取节点位置,通过Selenium函数定义位置获取信息,然后再通过正则表达式或字符串处理获取想要的值。如获取昵称:str_name = driver.find_element_by_xpath("//div[@class='ut']")#空格分隔 获取第一个值 "Eastmount 详细资料 设置 新手区"str_t = str_name.text.split(" ")num_name = str_t[0] print u'昵称: ' + num_name再如括号之间数字内容:#微博[294] 关注[351] 粉丝[294] 分组[1] @他的 str_gz = driver.find_element_by_xpath("//div[@class='tip2']/a[1]")guid = re.findall(pattern, str_gz.text, re.M)num_gz = int(guid[0])print u'关注数: ' + str(num_gz) 资料URL:http://weibo.cn/1644461042/info 关注URL:http://weibo.cn/1644461042/follow 粉丝URL:http://weibo.cn/1644461042/fans 但是手机端只能显示20页粉丝列表和关注列表。点击"资料"可以获取个人详细信息、点击"关注[516]"可以获取关注列表,如果需要建立不同用户之间的关注网,个人建议通过关注表而不是粉丝表,因为关注表覆盖明星更大,而粉丝太多,构建的图太稀疏。个人信息如下图所示: 获取微博微博URL:http://weibo.cn/guangxianliuyan?filter=0&page=1通过分析如下URL链接,可以发现Page=n 表示访问第n页微博,从而实现跳转。filter=1表示原创,可以分析它对应的开头几个类型。再通过函数获取内容:info = driver.find_elements_by_xpath("//div[@class='c']")然后如果发布的微博以"转发了..."开头表示转发的微博,否则为原创微博,代码:info.startswith(u'转发') 同时获取微博点赞数、转发数、时间等,其中因为转发会包括点赞数,故获取最后一个"赞[xxx]",然后通过正则表达式i获取括号之间内容。 str1 = info.split(u" 赞")[-1] if str1: val1 = re.match(, str1).groups()[0] print u'点赞数: ' + val1 infofile.write(u'点赞数: ' + str(val1) + '\r\n')微博都是以class='c'节点,故获取是有这类值,再进行字符串处理即可。 PS:最后希望文章对你有所帮助!其实方法很简单,希望你能理解这种思想,如何分析HTML源码及DOM树结构,然后动态获取自己需要的信息。(By:Eastmount 2016-02-23 深夜5点半 http://blog.csdn.net/eastmount/ )
word2vec相关基础知识、下载安装参考前文:word2vec词向量中文文本相似度计算目录: word2vec使用说明及源码介绍 1.下载地址 2.中文语料 3.参数介绍 4.计算相似词语 5.三个词预测语义语法关系 6.关键词聚类 1、下载地址 官网C语言下载地址:http://word2vec.googlecode.com/svn/trunk/ 运行 make 编译word2vec工具:Makefile的编译代码在makefile.txt文件中,先改名makefile.txt 为Makefile,然后在当前目录下执行make进行编译,生成可执行文件(编译过程中报出很出Warning,gcc不支持pthread多线程命令,注释即可)。再运行示例脚本:./demo-word.sh 和 ./demo-phrases.sh:a). 从http://mattmahoney.net/dc/text8.zip 在线下载了一个文件text8 ( 一个解压后不到100M的txt文件,可自己下载并解压放到同级目录下),可替换为自己的语料 b). 执行word2vec生成词向量到 vectors.bin文件中 c). 如果执行 sh demo-word.sh 训练生成vectors.bin文件后,下次可以直接调用已经训练好的词向量,如命令 ./distance vectors.bin2、中文语料 语料是我使用Selenium爬取的三大百科(百度、互动、维基)文本信息,其中每个百科有100个国家,总共300个国家(0001.txt~0300.txt),然后使用Jieba工具进行中文分词处理。最后输出Result_Country.txt文件,它把所有文本合并,共300行,每行对应一个国家的分词文本信息。3、参数介绍 下图参数源自文章:Windows下使用Word2vec继续词向量训练 - 一只鸟的天空Java推荐参考文章:word2vec使用指导 demo-word.sh文件,参考:http://jacoxu.com/?p=1084 make #if [ ! -e text8 ]; then # wget http://mattmahoney.net/dc/text8.zip -O text8.gz # gzip -d text8.gz -f #fi time ./word2vec -train Result_Country.txt -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15 ./distance vectors.bin 具体命令解释如下:-train Result_Country.txt 表示的是输入文件是Result_Country.txt -output vectors.bin 输出文件是vectors.bin -cbow 0 表示不使用cbow模型,默认为Skip-Gram模型 -size 200 每个单词的向量维度是200 -window 8 训练的窗口大小为8,就是考虑一个词前八个和后八个词语(实际代码中还有一个随机选窗口的过程,窗口大小小于等于5) -negative 0 表示是否使用NEG方,0表示不使用 -hs 1 是否使用HS方法,0表示不使用,1表示使用HS方法 -sample 指的是采样的阈值,如果一个词语在训练样本中出现的频率越大,那么就越会被采样 -binary 1 为1指的是结果二进制存储,为0是普通存储(普通存储的时候是可以打开看到词语和对应的向量的) 除了以上命令中的参数,word2vec还有几个参数对我们比较有用比如:-alpha 设置学习速率,默认的为0.025 –min-count 设置最低频率,默认是5,如果一个词语在文档中出现的次数小于5,那么就会丢弃 -classes 设置聚类个数,看了一下源码用的是k-means聚类的方法要注意-threads 20 线程数也会对结果产生影响。4、计算相似词语 命令:sh demo-word.sh demo-word.sh 中指令:make #if [ ! -e text8 ]; then # wget http://mattmahoney.net/dc/text8.zip -O text8.gz # gzip -d text8.gz -f #fi time ./word2vec -train Result_Country.txt -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15 ./distance vectors.bin 运行结果如下图所示: 如果想要不训练调用上次训练的vectors.bin文件,则输入 ./distance vectors.bin输入"阿富汗"输出相似次及相似距离,如"喀布尔"阿富汗首都,"坎大哈"阿富汗城市,类似中东国家"伊拉克"等。 输入"国歌"输出相似词如下图所示: 不仅仅名词可以获取相似词,动词也可以。如输入"位于",输出如下: distance.c 源码:// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <stdio.h> #include <string.h> #include <math.h> #include <malloc.h> const long long max_size = 2000; // max length of strings const long long N = 40; // number of closest words that will be shown const long long max_w = 50; // max length of vocabulary entries int main(int argc, char **argv) { FILE *f; char st1[max_size]; char *bestw[N]; char file_name[max_size], st[100][max_size]; float dist, len, bestd[N], vec[max_size]; long long words, size, a, b, c, d, cn, bi[100]; char ch; float *M; char *vocab; if (argc < 2) { printf("Usage: ./distance <FILE>\nwhere FILE contains word projections in the BINARY FORMAT\n"); return 0; } strcpy(file_name, argv[1]); f = fopen(file_name, "rb"); if (f == NULL) { printf("Input file not found\n"); return -1; } fscanf(f, "%lld", &words); fscanf(f, "%lld", &size); vocab = (char *)malloc((long long)words * max_w * sizeof(char)); for (a = 0; a < N; a++) bestw[a] = (char *)malloc(max_size * sizeof(char)); M = (float *)malloc((long long)words * (long long)size * sizeof(float)); if (M == NULL) { printf("Cannot allocate memory: %lld MB %lld %lld\n", (long long)words * size * sizeof(float) / 1048576, words, size); return -1; } for (b = 0; b < words; b++) { a = 0; while (1) { vocab[b * max_w + a] = fgetc(f); if (feof(f) || (vocab[b * max_w + a] == ' ')) break; if ((a < max_w) && (vocab[b * max_w + a] != '\n')) a++; } vocab[b * max_w + a] = 0; for (a = 0; a < size; a++) fread(&M[a + b * size], sizeof(float), 1, f); len = 0; for (a = 0; a < size; a++) len += M[a + b * size] * M[a + b * size]; len = sqrt(len); for (a = 0; a < size; a++) M[a + b * size] /= len; } fclose(f); while (1) { for (a = 0; a < N; a++) bestd[a] = 0; for (a = 0; a < N; a++) bestw[a][0] = 0; printf("Enter word or sentence (EXIT to break): "); a = 0; while (1) { st1[a] = fgetc(stdin); if ((st1[a] == '\n') || (a >= max_size - 1)) { st1[a] = 0; break; } a++; } if (!strcmp(st1, "EXIT")) break; cn = 0; b = 0; c = 0; while (1) { st[cn][b] = st1[c]; b++; c++; st[cn][b] = 0; if (st1[c] == 0) break; if (st1[c] == ' ') { cn++; b = 0; c++; } } cn++; for (a = 0; a < cn; a++) { for (b = 0; b < words; b++) if (!strcmp(&vocab[b * max_w], st[a])) break; if (b == words) b = -1; bi[a] = b; printf("\nWord: %s Position in vocabulary: %lld\n", st[a], bi[a]); if (b == -1) { printf("Out of dictionary word!\n"); break; } } if (b == -1) continue; printf("\n Word Cosine distance\n------------------------------------------------------------------------\n"); for (a = 0; a < size; a++) vec[a] = 0; for (b = 0; b < cn; b++) { if (bi[b] == -1) continue; for (a = 0; a < size; a++) vec[a] += M[a + bi[b] * size]; } len = 0; for (a = 0; a < size; a++) len += vec[a] * vec[a]; len = sqrt(len); for (a = 0; a < size; a++) vec[a] /= len; for (a = 0; a < N; a++) bestd[a] = -1; for (a = 0; a < N; a++) bestw[a][0] = 0; for (c = 0; c < words; c++) { a = 0; for (b = 0; b < cn; b++) if (bi[b] == c) a = 1; if (a == 1) continue; dist = 0; for (a = 0; a < size; a++) dist += vec[a] * M[a + c * size]; for (a = 0; a < N; a++) { if (dist > bestd[a]) { for (d = N - 1; d > a; d--) { bestd[d] = bestd[d - 1]; strcpy(bestw[d], bestw[d - 1]); } bestd[a] = dist; strcpy(bestw[a], &vocab[c * max_w]); break; } } } for (a = 0; a < N; a++) printf("%50s\t\t%f\n", bestw[a], bestd[a]); } return 0; } 5、三个词预测语义语法关系 命令:sh demo-analogy.shdemo-analogy.sh 中指令:make #if [ ! -e text8 ]; then # wget http://mattmahoney.net/dc/text8.zip -O text8.gz # gzip -d text8.gz -f #fi echo ------------------------------------------------------------------------------------- echo Note that for the word analogy to perform well, the model should be trained on much larger data set echo Example input: paris france berlin echo ------------------------------------------------------------------------------------- time ./word2vec -train Result_Country.txt -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15 ./word-analogy vectors.bin 运行结果如下图所示: 输入"韩国、首尔、日本"可以预测其首都"东京":韩国的首都是首尔 <==> 日本的首都是东京 输入"中国 亚洲 德国"可以预测语义语法关系"欧洲":中国位于亚洲 <==> 德国位于欧洲 如果输入仅仅2个词体会提示错误,同时输入"EXIT"可推出继续输入。 word-analogy.c 源码:// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include <stdio.h> #include <string.h> #include <math.h> #include <malloc.h> const long long max_size = 2000; // max length of strings const long long N = 40; // number of closest words that will be shown const long long max_w = 50; // max length of vocabulary entries int main(int argc, char **argv) { FILE *f; char st1[max_size]; char bestw[N][max_size]; char file_name[max_size], st[100][max_size]; float dist, len, bestd[N], vec[max_size]; long long words, size, a, b, c, d, cn, bi[100]; char ch; float *M; char *vocab; if (argc < 2) { printf("Usage: ./word-analogy <FILE>\nwhere FILE contains word projections in the BINARY FORMAT\n"); return 0; } strcpy(file_name, argv[1]); f = fopen(file_name, "rb"); if (f == NULL) { printf("Input file not found\n"); return -1; } fscanf(f, "%lld", &words); fscanf(f, "%lld", &size); vocab = (char *)malloc((long long)words * max_w * sizeof(char)); M = (float *)malloc((long long)words * (long long)size * sizeof(float)); if (M == NULL) { printf("Cannot allocate memory: %lld MB %lld %lld\n", (long long)words * size * sizeof(float) / 1048576, words, size); return -1; } for (b = 0; b < words; b++) { a = 0; while (1) { vocab[b * max_w + a] = fgetc(f); if (feof(f) || (vocab[b * max_w + a] == ' ')) break; if ((a < max_w) && (vocab[b * max_w + a] != '\n')) a++; } vocab[b * max_w + a] = 0; for (a = 0; a < size; a++) fread(&M[a + b * size], sizeof(float), 1, f); len = 0; for (a = 0; a < size; a++) len += M[a + b * size] * M[a + b * size]; len = sqrt(len); for (a = 0; a < size; a++) M[a + b * size] /= len; } fclose(f); while (1) { for (a = 0; a < N; a++) bestd[a] = 0; for (a = 0; a < N; a++) bestw[a][0] = 0; printf("Enter three words (EXIT to break): "); a = 0; while (1) { st1[a] = fgetc(stdin); if ((st1[a] == '\n') || (a >= max_size - 1)) { st1[a] = 0; break; } a++; } if (!strcmp(st1, "EXIT")) break; cn = 0; b = 0; c = 0; while (1) { st[cn][b] = st1[c]; b++; c++; st[cn][b] = 0; if (st1[c] == 0) break; if (st1[c] == ' ') { cn++; b = 0; c++; } } cn++; if (cn < 3) { printf("Only %lld words were entered.. three words are needed at the input to perform the calculation\n", cn); continue; } for (a = 0; a < cn; a++) { for (b = 0; b < words; b++) if (!strcmp(&vocab[b * max_w], st[a])) break; if (b == words) b = 0; bi[a] = b; printf("\nWord: %s Position in vocabulary: %lld\n", st[a], bi[a]); if (b == 0) { printf("Out of dictionary word!\n"); break; } } if (b == 0) continue; printf("\n Word Distance\n------------------------------------------------------------------------\n"); for (a = 0; a < size; a++) vec[a] = M[a + bi[1] * size] - M[a + bi[0] * size] + M[a + bi[2] * size]; len = 0; for (a = 0; a < size; a++) len += vec[a] * vec[a]; len = sqrt(len); for (a = 0; a < size; a++) vec[a] /= len; for (a = 0; a < N; a++) bestd[a] = 0; for (a = 0; a < N; a++) bestw[a][0] = 0; for (c = 0; c < words; c++) { if (c == bi[0]) continue; if (c == bi[1]) continue; if (c == bi[2]) continue; a = 0; for (b = 0; b < cn; b++) if (bi[b] == c) a = 1; if (a == 1) continue; dist = 0; for (a = 0; a < size; a++) dist += vec[a] * M[a + c * size]; for (a = 0; a < N; a++) { if (dist > bestd[a]) { for (d = N - 1; d > a; d--) { bestd[d] = bestd[d - 1]; strcpy(bestw[d], bestw[d - 1]); } bestd[a] = dist; strcpy(bestw[a], &vocab[c * max_w]); break; } } } for (a = 0; a < N; a++) printf("%50s\t\t%f\n", bestw[a], bestd[a]); } return 0; } 6、关键词聚类 命令:sh demo-classes.shdemo-classes.sh 中指令:make #if [ ! -e text8 ]; then # wget http://mattmahoney.net/dc/text8.zip -O text8.gz # gzip -d text8.gz -f #fi time ./word2vec -train Result_Country.txt -output classes.txt -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -iter 15 -classes 100 sort classes.txt -k 2 -n > classes.sorted.txt echo The word classes were saved to file classes.sorted.txt 运行结果如下图所示: 其中生词文件classes.txt和排序后的文件classes.sorted.txt: 聚类算法是Kmeans,类簇设置为100类,对应0~99,每类的关键词如下,但是如何计算300行数据每行对应的类标,还不太清楚~ 其中聚类代码见 word2vec.c 文件 void TrainModel() 函数: demo-phrases.sh(word2phrase.c) 是就是将词语拼成短语。 希望文章对你有所帮助,尤其是正在学习word2vec基础文章的。 推荐文章:文本深度表示模型Word2Vec - 小唯THU大神 利用中文数据跑Google开源项目word2vec - hebin大神 Word2vec在事件挖掘中的调研 - 热点事件推荐 (思路不错)(By:Eastmount 2016-02-20 深夜2点 http://blog.csdn.net/eastmount/ )
本文是讲述如何使用word2vec的基础教程,文章比较基础,希望对你有所帮助!官网C语言下载地址:http://word2vec.googlecode.com/svn/trunk/官网Python下载地址:http://radimrehurek.com/gensim/models/word2vec.html1.简单介绍 参考:《Word2vec的核心架构及其应用 · 熊富林,邓怡豪,唐晓晟 · 北邮2015年》 《Word2vec的工作原理及应用探究 · 周练 · 西安电子科技大学2014年》 《Word2vec对中文词进行聚类的研究 · 郑文超,徐鹏 · 北京邮电大学2013年》 PS:第一部分主要是给大家引入基础内容作铺垫,这类文章很多,希望大家自己去学习更多更好的基础内容,这篇博客主要是介绍Word2Vec对中文文本的用法。(1) 统计语言模型 统计语言模型的一般形式是给定已知的一组词,求解下一个词的条件概率。形式如下: 统计语言模型的一般形式直观、准确,n元模型中假设在不改变词语在上下文中的顺序前提下,距离相近的词语关系越近,距离较远的关联度越远,当距离足够远时,词语之间则没有关联度。但该模型没有完全利用语料的信息:1) 没有考虑距离更远的词语与当前词的关系,即超出范围n的词被忽略了,而这两者很可能有关系的。例如,“华盛顿是美国的首都”是当前语句,隔了大于n个词的地方又出现了“北京是中国的首都”,在n元模型中“华盛顿”和“北京”是没有关系的,然而这两个句子却隐含了语法及语义关系,即”华盛顿“和“北京”都是名词,并且分别是美国和中国的首都。 2) 忽略了词语之间的相似性,即上述模型无法考虑词语的语法关系。例如,语料中的“鱼在水中游”应该能够帮助我们产生“马在草原上跑”这样的句子,因为两个句子中“鱼”和“马”、“水”和“草原”、“游”和“跑”、“中”和“上”具有相同的语法特性。而在神经网络概率语言模型中,这两种信息将充分利用到。(2) 神经网络概率语言模型神经网络概率语言模型是一种新兴的自然语言处理算法,该模型通过学习训练语料获取词向量和概率密度函数,词向量是多维实数向量,向量中包含了自然语言中的语义和语法关系,词向量之间余弦距离的大小代表了词语之间关系的远近,词向量的加减运算则是计算机在"遣词造句"。神经网络概率语言模型经历了很长的发展阶段,由Bengio等人2003年提出的神经网络语言模型NNLM(Neural network language model)最为知名,以后的发展工作都参照此模型进行。历经十余年的研究,神经网络概率语言模型有了很大发展。如今在架构方面有比NNLM更简单的CBOW模型、Skip-gram模型;其次在训练方面,出现了Hierarchical Softmax算法、负采样算法(Negative Sampling),以及为了减小频繁词对结果准确性和训练速度的影响而引入的欠采样(Subsumpling)技术。 上图是基于三层神经网络的自然语言估计模型NNLM(Neural Network Language Model)。NNLM可以计算某一个上下文的下一个词为wi的概率,即(wi=i|context),词向量是其训练的副产物。NNLM根据语料库C生成对应的词汇表V。神将网络知识可以参考我的前文博客:神经网络和机器学习基础入门分享NNLM推荐Rachel-Zhang大神文章:word2vec——高效word特征求取近年来,神经网络概率语言模型发展迅速,Word2vec是最新技术理论的合集。Word2vec是Google公司在2013年开放的一款用于训练词向量的软件工具。所以,在讲述word2vec之前,先给大家介绍词向量的概念。(3) 词向量参考:licstar大神的NLP文章 Deep Learning in NLP (一)词向量和语言模型正如作者所说:Deep Learning 算法已经在图像和音频领域取得了惊人的成果,但是在 NLP 领域中尚未见到如此激动人心的结果。有一种说法是,语言(词、句子、篇章等)属于人类认知过程中产生的高层认知抽象实体,而语音和图像属于较为底层的原始输入信号,所以后两者更适合做deep learning来学习特征。但是将词用“词向量”的方式表示可谓是将 Deep Learning 算法引入 NLP 领域的一个核心技术。自然语言理解问题转化为机器学习问题的第一步都是通过一种方法把这些符号数学化。词向量具有良好的语义特性,是表示词语特征的常用方式。词向量的每一维的值代表一个具有一定的语义和语法上解释的特征。故可以将词向量的每一维称为一个词语特征。词向量用Distributed Representation表示,一种低维实数向量。例如,NLP中最直观、最常用的词表示方法是One-hot Representation。每个词用一个很长的向量表示,向量的维度表示词表大小,绝大多数是0,只有一个维度是1,代表当前词。 “话筒”表示为 [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 …] 即从0开始话筒记为3。 但这种One-hot Representation采用稀疏矩阵的方式表示词,在解决某些任务时会造成维数灾难,而使用低维的词向量就很好的解决了该问题。同时从实践上看,高维的特征如果要套用Deep Learning,其复杂度几乎是难以接受的,因此低维的词向量在这里也饱受追捧。 Distributed Representation低维实数向量,如:[0.792, −0.177, −0.107, 0.109, −0.542, …]。它让相似或相关的词在距离上更加接近。总之,Distributed Representation是一个稠密、低维的实数限量,它的每一维表示词语的一个潜在特征,该特征捕获了有用的句法和语义特征。其特点是将词语的不同句法和语义特征分布到它的每一个维度上去表示。 推荐我前面的基础文章:Python简单实现基于VSM的余弦相似度计算 (4) Word2vec参考:Word2vec的核心架构及其应用 · 熊富林,邓怡豪,唐晓晟 · 北邮2015年 Word2vec是Google公司在2013年开放的一款用于训练词向量的软件工具。它根据给定的语料库,通过优化后的训练模型快速有效的将一个词语表达成向量形式,其核心架构包括CBOW和Skip-gram。 在开始之前,引入模型复杂度,定义如下: O = E * T * Q 其中,E表示训练的次数,T表示训练语料中词的个数,Q因模型而异。E值不是我们关心的内容,T与训练语料有关,其值越大模型就越准确,Q在下面讲述具体模型是讨论。 NNLM模型是神经网络概率语言模型的基础模型。在NNLM模型中,从隐含层到输出层的计算时主要影响训练效率的地方,CBOW和Skip-gram模型考虑去掉隐含层。实践证明新训练的词向量的精确度可能不如NNLM模型(具有隐含层),但可以通过增加训练语料的方法来完善。 Word2vec包含两种训练模型,分别是CBOW和Skip_gram(输入层、发射层、输出层),如下图所示: CBOW模型:理解为上下文决定当前词出现的概率。在CBOW模型中,上下文所有的词对当前词出现概率的影响的权重是一样的,因此叫CBOW(continuous bag-of-words model)模型。如在袋子中取词,取出数量足够的词就可以了,至于取出的先后顺序是无关紧要的。Skip-gram模型:Skip-gram模型是一个简单实用的模型。为什么会提出该问题呢?在NLP中,语料的选取是一个相当重要的问题。 首先,语料必须充分。一方面词典的词量要足够大,另一方面尽可能地包含反映词语之间关系的句子,如“鱼在水中游”这种句式在语料中尽可能地多,模型才能学习到该句中的语义和语法关系,这和人类学习自然语言是一个道理,重复次数多了,也就会模型了。 其次,语料必须准确。所选取的语料能够正确反映该语言的语义和语法关系。如中文的《人民日报》比较准确。但更多时候不是语料选取引发准确性问题,而是处理的方法。 由于窗口大小的限制,这会导致超出窗口的词语与当前词之间的关系不能正确地反映到模型中,如果单纯扩大窗口大小会增加训练的复杂度。Skip-gram模型的提出很好解决了这些问题。 Skip-gram表示“跳过某些符号”。例如句子“中国足球踢得真是太烂了”有4个3元词组,分别是“中国足球踢得”、“足球踢得真是”、“踢得真是太烂”、“真是太烂了”,句子的本意都是“中国足球太烂”,可是上面4个3元组并不能反映出这个信息。 此时,使用Skip-gram模型允许某些词被跳过,因此可组成“中国足球太烂”这个3元词组。如果允许跳过2个词,即2-Skip-gram,那么上句话组成的3元词组为: 由上表可知:一方面Skip-gram反映了句子的真实意思,在新组成的这18个3元词组中,有8个词组能够正确反映例句中的真实意思;另一方面,扩大了语料,3元词组由原来的4个扩展到了18个。 语料的扩展能够提高训练的准确度,获得的词向量更能反映真实的文本含义。2.下载源码 下载地址:http://word2vec.googlecode.com/svn/trunk/ 使用SVN Checkout源代码,如下图所示。 3.中文语料 PS:最后附有word2vec源码、三大百科语料、腾讯新闻语料和分词python代码。 中文语料可以参考我的文章,通过Python下载百度百科、互动百科、维基百科的内容。 [python] lantern访问中文维基百科及selenium爬取维基百科语料 [Python爬虫] Selenium获取百度百科旅游景点的InfoBox消息盒 下载结果如下图所示,共300个国家,百度百科、互动百科、维基百科各自100个,对应的编号都是0001.txt~0100.txt,每个txt中包含一个实体(国家)的信息。 然后再使用Jieba分词工具对齐进行中文分词和文档合并。#encoding=utf-8 import sys import re import codecs import os import shutil import jieba import jieba.analyse #导入自定义词典 jieba.load_userdict("dict_all.txt") #Read file and cut def read_file_cut(): #create path pathBaidu = "BaiduSpiderCountry\\" resName = "Result_Country.txt" if os.path.exists(resName): os.remove(resName) result = codecs.open(resName, 'w', 'utf-8') num = 1 while num<=100: #5A 200 其它100 name = "%04d" % num fileName = pathBaidu + str(name) + ".txt" source = open(fileName, 'r') line = source.readline() while line!="": line = line.rstrip('\n') #line = unicode(line, "utf-8") seglist = jieba.cut(line,cut_all=False) #精确模式 output = ' '.join(list(seglist)) #空格拼接 #print output result.write(output + ' ') #空格取代换行'\r\n' line = source.readline() else: print 'End file: ' + str(num) result.write('\r\n') source.close() num = num + 1 else: print 'End Baidu' result.close() #Run function if __name__ == '__main__': read_file_cut() 上面只显示了对百度百科100个国家进行分词的代码,但核心代码一样。同时,如果需要对停用词过滤或标点符号过滤可以自定义实现。 分词详见: [python] 使用Jieba工具中文分词及文本聚类概念 分词合并后的结果为Result_Country.txt,相当于600行,每行对应一个分词后的国家。 4.运行源码 强烈推荐三篇大神介绍word2vec处理中文语料的文章,其中Felven好像是师兄。 Windows下使用Word2vec继续词向量训练 - 一只鸟的天空 利用word2vec对关键词进行聚类 - Felven http://www.52nlp.cn/中英文维基百科语料上的word2vec实验 word2vec 词向量工具 - 百度文库因为word2vec需要linux环境,所有首先在windows下安装linux环境模拟器,推荐cygwin。然后把语料Result_Country.txt放入word2vec目录下,修改demo-word.sh文件,该文件默认情况下使用自带的text8数据进行训练,如果训练数据不存在,则会进行下载,因为需要使用自己的数据进行训练,故注释掉下载代码。demo-word.sh文件修改如下:make #if [ ! -e text8 ]; then # wget http://mattmahoney.net/dc/text8.zip -O text8.gz # gzip -d text8.gz -f #fi time ./word2vec -train Result_Country.txt -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15 ./distance vectors.bin 下图参数源自文章:Windows下使用Word2vec继续词向量训练 - 一只鸟的天空 运行命令sh demo-word.sh,等待训练完成。模型训练完成之后,得到了vectors.bin这个词向量文件,可以直接运用。 5.结果展示 通过训练得到的词向量我们可以进行相应的自然语言处理工作,比如求相似词、关键词聚类等。其中word2vec中提供了distance求词的cosine相似度,并排序。也可以在训练时,设置-classes参数来指定聚类的簇个数,使用kmeans进行聚类。cd C:/Users/dell/Desktop/word2vec sh demo-word.sh ./distance vectors.bin 输入阿富汗:喀布尔(首都)、坎大哈(主要城市)、吉尔吉斯斯坦、伊拉克等。 输入国歌: 输入首都: 输入GDP: 最后希望文章对你有所帮助,主要是使用的方法。同时更多应用需要你自己去研究学习。 word2vec源码、语料下载地址: http://download.csdn.net/detail/eastmount/9434889(By:Eastmount 2016-02-18 深夜1点 http://blog.csdn.net/eastmount/ )
希望文章对你有所帮助,尤其是web爬虫初学者和NLP相关同学。当然你也能,懂的~ 目录: 0 前言 1 lantern访问中文维基百科 2 Selenium调用Chrome自动访问维基百科 3 Selenium爬取维基百科信息 代码及软件下载地址:http://download.csdn.net/detail/eastmount/94228750 前言 在对海量知识挖掘和自然语言处理(Natural Language Processing,简称NLP)中会大量涉及到三大百科的语料问题,尤其是中文汉字语料问题,包括:百度百科、互动百科和维基百科。其应用涉及命名实体(Named Entity,简称NE)消歧、实体对齐、双语机器翻译、推荐系统、情感分析、知识图谱等领域。其中三大百科准确率方面是维基百科>互动百科>百度百科;中文涉及实体内容是百度百科>互动百科>维基百科。如下图所示,维基百科Wikipedia页面通常包括:Title(标题)、Description(摘要描述)、InfoBox(消息盒)、Categories(实体类别)、Crosslingual Links(跨语言链接)等。 前面我讲述了很多关于Python和Selenium爬取百度百科InfoBox、生物医疗PubMed、虎扑图片的例子,虽然效率不高,但是勉强能够进行。同样你可以通过Selenium爬取自己实验的语料,设置不同的主题,再进行文本聚类、LDA主题分布、实体消歧等。 但问题来了:中文维基百科总是被屏蔽,第一个问题就是如何访问中文维基百科呢? 1 lantern访问中文维基百科 官文网址:https://www.getlantern.org/ 参考文章:http://www.iyaxi.com/2015-11-17/732.html 下载地址:http://pan.baidu.com/s/1hrgqgGcLantern:一款免费强大的帆樯软件,译为“灯笼”。点击exe即可运行,非常巧小的一款软件;据说是google公司推出的,但确实良心制作。 安装运行后,会跳出如下页面:http://127.0.0.1:16823/ 同时你可以访问YouTube和维基百科了,就是这么简单~ 2 Selenium调用chrome自动访问维基百科 Selenium调用Firefox浏览器直接设置代码"driver = webdriver.Firefox()"即可,但是调用Chrome需要下载chromedriver.exe驱动。同时放置于Chrome安装目录下: 参考:http://download.csdn.net/download/qianaier/7966945 我的目录:C:\Program Files (x86)\Google\Chrome\Application,如果你报错: 同时添加该路径到环境变量path中: 下面简单四句代码即可调用浏览器自动访问维基百科页面:from selenium import webdriver driver = webdriver.Chrome() url_path = "https://zh.wikipedia.org/" driver.get(url_path) 3 Selenium爬取维基百科内容 爬取的方法与前面百度百科的方法类似,现在txt中定义实体名称,即需要爬取的网页名称,再去到具体页面爬取内容。这里需要讲述一个方法: https://zh.wikipedia.org/wiki/阿富汗 可以访问中国页面,同样该方法适用于互动百科,即:url+文件中读取实体名称 当然百度百科,你只需要获取输入框的按钮信息,然后输入读取文件名称,自动回车或点击即可。其中你可能会遇到一些存在歧义的页面,再处理下即可。同时维基百科和互动百科中存在分类页面,归纳了自己想要的内容,也比较方便。 最后是代码及运行结果:# coding=utf-8 """ Created on 2016-01-30 @author: Eastmount """ import time import re import os import sys import codecs from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Chrome() wait = ui.WebDriverWait(driver,10) #Get the infobox def getInfobox(name, fileName): try: print u'文件名称: ', fileName info = codecs.open(fileName, 'w', 'utf-8') print name.rstrip('\n') driver.get("https://zh.wikipedia.org/wiki/"+name) info.write(name.rstrip('\n')+'\r\n') #codecs不支持'\n'换行 #print driver.current_url #爬取文本信息 共10段信息 elem_value = driver.find_elements_by_xpath("//div[@id='mw-content-text']/p") num = 0 for value in elem_value: print value.text info.writelines(value.text + '\r\n') if num>=9: break num+=1 time.sleep(1) except Exception,e: #'utf8' codec can't decode byte print "Error: ",e finally: print '\n' info.close() #Main function def main(): #By function get information path = "WikipediaSpiderSpots\\" if os.path.isdir(path): shutil.rmtree(path, True) os.makedirs(path) source = open("Tourist_country_5A_Wiki.txt", 'r') num = 1 for entityName in source: entityName = unicode(entityName, "utf-8") if u'阿富汗' in entityName: entityName = u'阿富汗' name = "%04d" % num fileName = path + str(name) + ".txt" getInfobox(entityName, fileName) num = num + 1 print 'End Read Files!' source.close() driver.close() if __name__ == '__main__': main() 运行结果如下图,共爬取了每个实体的10段信息,而百度摘要会有id,可只爬取摘要。 同样,你也可以通过下面这部分代码爬取InfoBox信息,首先给出html源码标签: 核心代码:elem_value = driver.find_elements_by_xpath("//table[@class='infobox']") 具体代码如下:# coding=utf-8 """ Created on 2016-01-30 @author: Eastmount """ import time import re import os import sys import codecs from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Chrome() wait = ui.WebDriverWait(driver,10) #Get the infobox def getInfobox(name, fileName): try: print u'文件名称: ', fileName info = codecs.open(fileName, 'w', 'utf-8') print name.rstrip('\n') driver.get("https://zh.wikipedia.org/wiki/"+name) info.write(name.rstrip('\n')+'\r\n') #codecs不支持'\n'换行 #print driver.current_url #爬取文本信息 共10段信息 elem_value = driver.find_elements_by_xpath("//table[@class='infobox']") for value in elem_value: print value.text info.writelines(value.text + '\r\n') time.sleep(1) except Exception,e: #'utf8' codec can't decode byte print "Error: ",e finally: print '\n' info.close() #Main function def main(): #By function get information path = "WikipediaSpiderSpots\\" if os.path.isdir(path): shutil.rmtree(path, True) os.makedirs(path) source = open("Tourist_country_5A_Wiki.txt", 'r') num = 1 for entityName in source: entityName = unicode(entityName, "utf-8") if u'阿富汗' in entityName: entityName = u'阿富汗' name = "%04d" % num fileName = path + str(name) + ".txt" getInfobox(entityName, fileName) num = num + 1 print 'End Read Files!' source.close() driver.close() if __name__ == '__main__': main() 运行结果如下图所示: 最后希望文章对你有所帮助,还是那句话:如果刚好你遇到这个问题,就会受益匪浅;否则感觉没什么技术含量,不过确实也挺简单的,但是灯笼还是非常强大的。一月份终于要结束了,不知道多少个熬夜到早上10点,回家好好享受下生活吧~(By:Eastmount 2016-01-30 早上8点 http://blog.csdn.net//eastmount/ )
本文主要讲述了SQL查询语句表之间的行列转换,同时也包括如何将一行数据转换成两列数据的方法、子查询的应用、decode函数的用法。希望文章对你有所帮助~ 1.创建数据库表及插入数据 2.子查询统计不同性质的学生总数 3.一行数据转换成两列数据 union all 4.表行列数据转换(表转置) 1.创建数据库表及插入数据 创建数据库、创建学生表并设置主键、插入数据代码如下: --创建数据库 create database StudentMS --使用数据库 use StudentMS --创建学生表 (属性:姓名、学号(pk)、学院、出生日期、性别、籍贯) create table xs ( name varchar(10) not null, id varchar(10) not null, xy varchar(10), birthday datetime, xb char(2), jg varchar(8) ) --创建学生表主键:学号 alter table xs add constraint pk_xs primary key(id) --插入数据 insert into xs (id, name, xb, birthday, xy, jg) values('1160001', '刘备', '男', '1991-11-5', '软件学院', '河北省'); 输出数据如下图所示: 2.子查询统计不同性质的学生总数 使用子查询统计不同学院总人数、不同性别总人数和河北/河南学生总人数。 --子查询统计人数 select a.a_num as 软院人数, b.b_num as 计院人数, c.c_num as 自动化人数, d.d_num as 男生人数, e.e_num as 女生人数, f.f_num as 河北河南人数 from (select count(*) as a_num from xs where xy='软件学院') a, (select count(*) as b_num from xs where xy='计算机学院') b, (select count(*) as c_num from xs where xy='自动化学院') c, (select count(*) as d_num from xs where xb='男') d, (select count(*) as e_num from xs where xb='女') e, (select count(*) as f_num from xs where jg in ('河北省','河南省')) f; 输出结果: PS:若中文汉字太长报错,则需引用双引号。如:select num as "项目(文化学术讲座)"3.一行数据转换成两列数据 这时,项目SQL语句的需要是显示成两列如下图所示: 其实简单编写SQL语句,前端再处理这些数据更加方便,当然SQL也是能处理的。 当时走进了一个误区,认为"软件人数"是select中as自定义的一行数据的属性,如何显示在表中呢?当时是通过Oracle方法decode自定义显示的,其实直接输出,union all取代子查询即可。当然union all其它表也可以继续添加。 select '软院人数' as "统计类别", count(*) as "数量" from xs where xy='软件学院' union all select '计院人数', count(*) from xs where xy='计算机学院' union all select '自动化人数', count(*) from xs where xy='自动化学院' union all select '男生人数', count(*) from xs where xb='男' union all select '女生人数', count(*) from xs where xb='女' union all select '河北河南人数', count(*) from xs where jg in ('河北省','河南省'); 这里我简单给大家回顾下UNION ALL方法:(参考:MIN飞翔博客)UNION: (1) 其目的是将两个SQL语句的结果合并起来; (2) 它的一个限制是两个SQL语句所产生的栏位需要是同样的资料种类; (3) UNION只是将两个结果联结起来一起显示,并不是联结两个表; (4) UNION在进行表链接后会筛选掉重复的记录。UNION ALL: (1) 这个指令的目的也是要将两个 SQL 语句的结果合并在一起; (2) UNION ALL 和 UNION 不同之处在于 UNION ALL 会将每一个符合条件的资料都列出来,无论资料值有无重复; (3) UNION ALL只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。 从效率上说,sql union all的执行效率要比sql union效率要高很多,这是因为使用sql union需要进行排重,而sql union All 是不需要排重的,这一点非常重要,因为对于一些单纯地使用分表来提高效率的查询,完全可以使用sql union All。 补充:(摒弃的代码) 当时使用decode函数,如果KWHD_WH_XZ='校级',则输出自定义值'校级总数',否则输出原始值;同时通过group by获取该列所有值,sum(decode(t.KWHD_WH_XZ,'校级',1,0)计算校级的个数。select whxs1.num1 as 项目名称, whxs2.num2 as 数量 from (select decode(KWHD_WH_XZ, '校级', '校级总数', KWHD_WH_XZ) as num1 from T_WSTB_KWHD_1 t where KWHD_WH_XZ='校级' group by KWHD_WH_XZ) whxs1, (select sum(decode(t.KWHD_WH_XZ,'校级',1,0)) as num2 from T_WSTB_KWHD_1 t where KWHD_WH_XZ='校级' group by KWHD_WH_XZ ) whxs2; 输出如下,但是再添加一行数据如何实现呢?所以还是推荐UNION ALL。 4.表行列数据转换(表转置) 参考:http://blog.163.com/dreamman_yx/blog/static/26526894201121595846270 SQL语句如下:select country, sum(case when type='A' then money end) as A, sum(case when type='B' then money end) as B, sum(case when type='C' then money end) as C from table1 group by country 另一种方法源自文章:http://blog.sina.com.cn/s/blog_63772d910100pmln.html 方法介绍:decode(条件,值1,结果1,值2,结果2,值3,结果3,... 值n,结果n,缺省值) 函数类比: IF 条件=值1 THEN RETURN(结果1) ELSIF 条件=值2 THEN RETURN(结果2) ...... ELSIF 条件=值n THEN RETURN(结果n) ELSE RETURN(缺省值) END IF 举个例子如下: SQL语句如下,其中sum(decode(t.result,'胜',1,0))表示result字段如果值为“胜”,则decode的结果值为1,否则取缺省值0,最后sum统计加和。select name as 姓名,sum(decode(t.result,'胜',1,0)) as 胜,sum(decode(t.result,'负',1,0)) as 负 from t_result t group by name order by 胜 desc,负 asc 最后希望文章对你有所帮助,其实SQL语句中还是有很多非常高深的变化,目前只窥得一二啊!fighting...O(∩_∩)O(By:Eastmount 2016-01-22 深夜5点 http://blog.csdn.net//eastmount/ )
0 前言 本文主要讲述以下几点: 1.通过scikit-learn计算文本内容的tfidf并构造N*M矩阵(N个文档 M个特征词); 2.调用scikit-learn中的K-means进行文本聚类; 3.使用PAC进行降维处理,每行文本表示成两维数据; 4.最后调用Matplotlib显示聚类效果图。文章更详细的内容参考:http://blog.csdn.net/eastmount/article/details/50473675由于涉及到了我的毕业设计,一些更深入的内容和详细的信息,我这里就不再详细叙述了,毕竟还要查重和未发表。但其中一些算法的实现步骤是很清晰的。最后希望文章对你有所帮助,尤其是那些正在学习文本相似度计算或文本聚类的初学者。1 输入文本输入是读取本地的01_All_BHSpider_Content_Result.txt文件,里面包括1000行数据,其中001~400行为景区、401~600为动物、601~800为人物明星、801~1000为国家地理文本内容(百度百科摘要信息)。 该内容可以自定义爬虫进行爬取,同时分词采用Jieba进行。 免费下载包括代码py文件和01_All_BHSpider_Content_Result.txt。 下载地址:http://download.csdn.net/detail/eastmount/94108102 源代码 代码如下,详见注释和后面的学习笔记推荐: # coding=utf-8 """ Created on 2016-01-16 @author: Eastmount 输入:打开 All_BHSpider_Result.txt 对应1000个文本 001~400 5A景区 401~600 动物 601~800 人物 801~1000 国家 输出:BHTfidf_Result.txt tfidf值 聚类图形 1000个类标 参数:weight权重 这是一个重要参数 """ import time import re import os import sys import codecs import shutil import numpy as np import matplotlib import scipy import matplotlib.pyplot as plt from sklearn import feature_extraction from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import HashingVectorizer if __name__ == "__main__": ######################################################################### # 第一步 计算TFIDF #文档预料 空格连接 corpus = [] #读取预料 一行预料为一个文档 for line in open('01_All_BHSpider_Content_Result.txt', 'r').readlines(): #print line corpus.append(line.strip()) #print corpus #参考: http://blog.csdn.net/abcjennifer/article/details/23615947 #vectorizer = HashingVectorizer(n_features = 4000) #将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频 vectorizer = CountVectorizer() #该类会统计每个词语的tf-idf权值 transformer = TfidfTransformer() #第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵 tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus)) #获取词袋模型中的所有词语 word = vectorizer.get_feature_names() #将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重 weight = tfidf.toarray() #打印特征向量文本内容 print 'Features length: ' + str(len(word)) resName = "BHTfidf_Result.txt" result = codecs.open(resName, 'w', 'utf-8') for j in range(len(word)): result.write(word[j] + ' ') result.write('\r\n\r\n') #打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重 for i in range(len(weight)): #print u"-------这里输出第", i, u"类文本的词语tf-idf权重------" for j in range(len(word)): #print weight[i][j], result.write(str(weight[i][j]) + ' ') result.write('\r\n\r\n') result.close() ######################################################################## # 第二步 聚类Kmeans print 'Start Kmeans:' from sklearn.cluster import KMeans clf = KMeans(n_clusters=4) #景区 动物 人物 国家 s = clf.fit(weight) print s ''' print 'Start MiniBatchKmeans:' from sklearn.cluster import MiniBatchKMeans clf = MiniBatchKMeans(n_clusters=20) s = clf.fit(weight) print s ''' #中心点 print(clf.cluster_centers_) #每个样本所属的簇 label = [] #存储1000个类标 4个类 print(clf.labels_) i = 1 while i <= len(clf.labels_): print i, clf.labels_[i-1] label.append(clf.labels_[i-1]) i = i + 1 #用来评估簇的个数是否合适,距离越小说明簇分的越好,选取临界点的簇个数 958.137281791 print(clf.inertia_) ######################################################################## # 第三步 图形输出 降维 from sklearn.decomposition import PCA pca = PCA(n_components=2) #输出两维 newData = pca.fit_transform(weight) #载入N维 print newData #5A景区 x1 = [] y1 = [] i=0 while i<400: x1.append(newData[i][0]) y1.append(newData[i][1]) i += 1 #动物 x2 = [] y2 = [] i = 400 while i<600: x2.append(newData[i][0]) y2.append(newData[i][1]) i += 1 #人物 x3 = [] y3 = [] i = 600 while i<800: x3.append(newData[i][0]) y3.append(newData[i][1]) i += 1 #国家 x4 = [] y4 = [] i = 800 while i<1000: x4.append(newData[i][0]) y4.append(newData[i][1]) i += 1 #四种颜色 红 绿 蓝 黑 plt.plot(x1, y1, 'or') plt.plot(x2, y2, 'og') plt.plot(x3, y3, 'ob') plt.plot(x4, y4, 'ok') plt.show() 3 输出结果采用Kmeans中设置类簇数为4,分别表示景区、动物、明星和国家。 其中运行结果如下图所示,包括17900维tfidf特征向量: 聚类输出结果如下图所示:其中"红-景区 绿-动物 蓝-人物 黑-国家"。由于数据集比较小,文本聚类效果还是很明显的,而LDA算法是计算每个主题分布的算法,推荐你也去学习下。 4 性能评估这里我想结合文本聚类简单叙述下最常用的评估方法: 正确率 Precision = 正确识别的个体总数 / 识别出的个体总数 召回率 Recall = 正确识别的个体总数 / 测试集中存在的个体总数 F值 F-measure = 正确率 * 召回率 * 2 / (正确率 + 召回率) 由于"clf.labels_"会返回聚类每个样本所属的簇,比如1000行数据,就会返回1000个label值。同时,clf = KMeans(n_clusters=4)设置了类簇为4,故每个值对应在0、1、2、3中的一个,统计结果如下: 其中以世界国家为例,label1数目为198,同时识别出的个体数=198(世界国家)+2(动物)=200,故: 准确率=198/200=0.990 其中动物里面有两个聚类到了世界国家中。而召回率我以人物明星为例,因为知道测试集中601~800这200个数据对应人物明星,故测试集中存在个体数为200,而正确识别数目为185个,故: 召回率=185/200=0.925 最后计算F值即可。同时可以计算宏平均聚类准确率(Macro-Prec)和宏平均召回率(Macro-Rec)。 5 总结及推荐学习资料 代码中有几个问题我没有实现,包括: (1) 使用HashingVectorizer(n_features = n)设置维数,如何选择更合理的特征; (2) 调用plt.legend([plot1, plot2, plot3, plot4], (u'景区', u'动物', u'明星', u'国家') ) 报错"AttributeError: 'NoneType' object has no attribute 'tk'"; (3) sklearn其它聚类算法以及设置聚类中心点。 但是对那些刚接触Python聚类算法的同学 ,这篇文章还是有一定帮助的!同时也是我自己的在线笔记,仅仅提供了一条基础介绍,希望你能从这篇文章入门,从而实现你自己做的东西。最近总是深夜编码,生活太忙太充实,写篇博客放松下心情也不错~ 最后推荐一些相关资料: 用Python开始机器学习(10:聚类算法之K均值) -lsldd大神 应用scikit-learn做文本分类(特征提取 KNN SVM 聚类) - Rachel-Zhang大神 Scikit Learn: 在python中机器学习(KNN SVMs K均) - yyliu大神 开源中国 【机器学习实验】scikit-learn的主要模块和基本使用 - JasonDing大神 Scikit-learn学习笔记 中文简介(P30-Cluster) - 百度文库 使用sklearn做kmeans聚类分析 - xiaolitnt 使用sklearn + jieba中文分词构建文本分类器 - MANYU GOU大神 sklearn学习(1) 数据集(官方数据集使用) - yuanyu5237大神 scikit-learn使用笔记与sign prediction简单小结 - xupeizhi http://scikit-learn.org/stable/modules/clustering.html#clustering 基于K-Means的文本聚类(强推基础介绍) - freesum Python图表绘制:matplotlib绘图库入门 - 360图书 Stanford机器学习---第十讲. 数据降维 - Rachel-Zhang大神 聚类算法初探(七)聚类分析的效果评测 - 皮果提大神 python使用matplotlib绘图 -- barChart Python-Matplotlib安装及简单使用 (绘制柱状图) scikit-learn中PCA的使用方法 - wphh (推荐阅读) 使用 PCA 进行降维处理——基于 sklearn 库 - Guo'Blog(By:Eastmount 2016-01-20 深夜5点 http://blog.csdn.net//eastmount/ )
最近SQL语句写得比较多,也发现了自己的很多不足之处。在此先写一篇关于SQL语句的在线笔记,方便大家学习和后面的工作,SQL Server、MySQL、Oracle基本语法都类似,接下来我需要阅读《SQL Server性能优化与管理的艺术》。 最后,希望这篇文章对你有所帮助吧!重点是select语句的用法。目录如下: 一.创建数据库和表 1.创建数据库 2.创建表并设置主键 3.插入数据 二.select查询操作 1.通过日期计算年龄 2.获取某列所有不同的属性值 group by 3.查询字符串匹配like和多值属性判断in 4.查询输出某列属性中某个特定值 5.子查询统计不同阶段学生总数 6.使用子查询按行动态输出学院相关信息 7.Oracle数据库null设置成自定义值的方法 8.Oracle计算百分比方法 9.Oracle查询除法运算 10.Oracle统计某个属性逗号分隔值的个数 一. 创建数据库和表 1.创建数据库 --创建数据库 create database StudentMS --使用数据库 use StudentMS --删除数据库 --drop database StudentMS 2.创建表并设置主键(外键类似)--创建学生表 (属性:姓名、学号(pk)、学院、出生日期、性别、籍贯) create table xs ( name varchar(10) not null, id varchar(10) not null, xy varchar(10), birthday datetime, xb char(2), jg varchar(8) ) --创建学生表主键:学号 alter table xs add constraint pk_xs primary key(id) --创建表学生表外键:系代号 此表中xdh已被省略 alter table xs add constraint fk_xs foreign key(xdh) references xb (xdh) 3.插入数据insert into xs (id, name, xb, birthday, xy, jg) values('1160001', '刘备', '男', '1991-11-5', '软件学院', '河北省'); 总共插入10个学生的数据,其中如birthday可为null,如下: 二. select查询操作 1.通过日期计算年龄 通过 (当前日期-出生日期) 两种方法: 1). year(getdate()) - year(birthday) 2). datediff(YY, birthday, getdate()) 代码如下:select id as 学号, name as 姓名, year(getdate())-year(birthday) as 年龄, birthday as 出生日期 from xs; select id as 学号, name as 姓名, datediff(YY,birthday,getdate()) as 年龄, birthday as 出生日期 from xs; 输出如下所示,后面也可以计算不同年龄段的人数: 注意:Oracle会报错“ORA-00904: 'DATEDIFF' invalid identifier”, 它的方法如下: Trunc(MONTHS_BETWEEN(SYSDATE, BIRTH_DATE)/12) 函数Trunc在这里对带有小数位数的数字取整数部分,SYSDATE为oracle的获取当前日期的函数,BIRTH_DATE为我自己的数据库表中存储生日日期的字段。 判断年份等于当前年份方法:YEAR=to_char(sysdate, 'yyyy')2.获取某列所有不同的属性值 group by 它的功能包括:获取学院列所有学院信息,也可以用来统计所有学生同名的人数。--方法1:group by 列分组 select xy from xs group by xy; --方法2:列出不同的值 select distinct xy from xs; 输出结果: 统计不同学院的人数信息:select xy as 学院, count(*) as 总人数 from xs group by xy; PS:如果需要排序可以添加order by xy,而统计重名学生可通过having count(*)>1。 推荐:http://www.cnblogs.com/rainman/archive/2013/05/03/3058451.html3.查询字符串匹配like和多值属性判断in LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式:SQL LIKE 操作符 - w3school--匹配姓名以"黄"开头的学生 select * from xs where name like '黄%'; --匹配学院包含"计算机"的学生 select * from xs where xy like '%计算机%'; --匹配姓名以"尚香"结尾的学生 select * from xs where name like '%尚香'; 输出结果: LIKE匹配某个字段的变量的方法:DL_BHXNZYMC like '%' || ZY_NAME ||'%'select T_WSTB_DLPYJBQKB.DL_BHXNZYMC, ZY_NAME from T_WSTB_ZYJBQK, T_WSTB_DLPYJBQKB where T_WSTB_DLPYJBQKB.DL_BHXNZYMC like '%' || ZY_NAME ||'%' 输出如下所示,也可以某个字段包含的个数: IN 操作符允许我们在 WHERE 子句中规定多个值,换种说法就是替换or:select * from xs where jg in ('河北省','河南省'); 输出结果: 4.查询输出某列属性中的某个特定值 比如我希望输出软件学院这个值,可以使用group by分组再定义这个值。其缺点是这个值必需是定义存在的,当然如果C#或JAVA可以定义变量连接这个值。select xy as 学院名称 from xs where xy='软件学院' group by xy; 输出结果: 5.子查询统计不同阶段学生总数 使用子查询统计不同学院总人数、不同性别总人数和河北/河南学生总人数。--子查询统计人数 select a.a_num as 软院人数, b.b_num as 计院人数, c.c_num as 自动化人数, d.d_num as 男生人数, e.e_num as 女生人数, f.f_num as 河北河南人数 from (select count(*) as a_num from xs where xy='软件学院') a, (select count(*) as b_num from xs where xy='计算机学院') b, (select count(*) as c_num from xs where xy='自动化学院') c, (select count(*) as d_num from xs where xb='男') d, (select count(*) as e_num from xs where xb='女') e, (select count(*) as f_num from xs where jg in ('河北省','河南省')) f; 输出结果: PS:当时需要设计一条SQL,统计各个学院的教师总数、高级职称教师总数、35岁以下青年教师总数、教授教师总数;而且输出是一行,每个学院共5个值,例如: 上面这种显示方法非常局限,不能实现动态的查询,如果增加新的学院,你SQL语句中 where xy='软件学院' 需要相应修改,如果是连接前端建议使用逗号连接查询。当然,SQL语句更理想的输出如下:(参考6) 6.使用子查询按行动态输出学院相关信息 获取每个学院总人数、男生总人数、小于等于25岁的总人数和生源地河北河南人数。 这种方法通常是多个表之间的夸表查询,首先创建一个学院表:学院名称和学院代码。--创建学院表 create table table_xy ( name varchar(10) not null, id varchar(10) not null ); --插入数据 insert into table_xy(id, name) values('001', '软件学院'); insert into table_xy(id, name) values('002', '计算机学院'); insert into table_xy(id, name) values('003', '自动化学院'); insert into table_xy(id, name) values('004', '法学院'); 输出如下,这里插入一个法学院,它的统计结果都为空: 然后,子查询SQL语句如下:select distinct name as 学院名称, (select count(*) from xs where xs.xy=table_xy.name) as 总人数, (select count(*) from xs where xs.xy=table_xy.name and xs.xb='男') as 男生总数, (select count(*) from xs where xs.xy=table_xy.name and datediff(YY,birthday,getdate())<=25) as 二十五岁人数, (select count(*) from xs where xs.xy=table_xy.name and xs.jg in ('河北省','河南省')) as 河北河南生源地 from table_xy; 输出结果: 7.Oracle数据库null设置成自定义值的方法方法包括主要有两个,参考:http://www.soso.io/article/72986.html1). nvl(expr1, expr2)若EXPR1是NULL,則返回EXPR2,否則返回EXPR1。nvl(person_name,“未知”)表示若person_name字段值为空时返回“未知”,如不为空则返回person_name的字段值。通过这个函数可以定制null的排序位置。 |2). decode(DEPARTMENT_NAME, null, 'NULL', DEPARTMENT_NAME)如果部门名称在表中值为null,则用NULL替代,也可设置为"空"各种自定义字符串。decode函数比nvl函数更强大,同样它也可以将输入参数为空时转换为一特定值。 如decode(person_name,null,“未知”, person_name)表示当person_name为空时返回“未知”,如不为空则返回person_name的字段值。 PS:而SQL Server中没有函数decode,但是其实质可以通过case when来实现和替代。参考:http://blog.csdn.net/hu_shengyang/article/details/105338658.Oracle计算百分比方法 核心SQL语句如下:to_char(trunc(NUM/ALL_NUM*100, 2)) || '% 其中NUM除以ALL_NUM总数,并且保留两位有效数字,如下图所示: 9.Oracle查询除法运算 主要语句:select a/b from c; 如:1.0*男生人数/ 人总数*100,使用trunc主要是小数点保留两位有效数字。 如果出现错误:[Err]ORA-01476: divisor is equal to zero,可修改为:select decode(b,0,0,a/b) from c。 select t1.ZFJGSL as 数量, trunc( 1.0 * (select ITEM_VALUE from T_WSTB_YJBKBYSJYQK where RECORD_YEAR=(to_char(sysdate, 'yyyy')-2) and FIRST_NUM='2' and SECOND_NUM='2') / (select ITEM_VALUE from T_WSTB_YJBKBYSJYQK where RECORD_YEAR=(to_char(sysdate, 'yyyy')-2) and FIRST_NUM='2' and SECOND_NUM='1') * 100, 2) as 比例 from (select ITEM_VALUE as ZFJGSL from T_WSTB_YJBKBYSJYQK where RECORD_YEAR=(to_char(sysdate, 'yyyy')-2) and FIRST_NUM='2' and SECOND_NUM='2') t1; 输出如下所示: 10.Oracle统计某个属性逗号分隔值的个数 如下图所示,学科大类中包括各个学科专业名称,通过逗号分隔,如何统计个数呢? SQL代码如下,Orcale使用length,其他是len函数:select DL_NAME as num1, length(DL_BHXNZYMC)-length(replace(DL_BHXNZYMC,',',''))+1 as num2 from T_WSTB_DLPYJBQKB 运行结果如下所示: 三. 总结 最后希望文章对你有所帮助,这是一篇我的在线笔记,同时后面结合自己实际项目和SQL性能优化,将分享一些更为专业的文章~ 最近真的太忙了,做自己的毕设、学校的项目、帮别人毕设或项目解惑,虽然累,但感觉还是非常充实的;买了本《邓肯专·永不退场》,有机会再品味吧!同时每天刀两把,周末搞个小火锅,这也是生活啊!(*^__^*) 嘻嘻.......(By:Eastmount 2016-01-17 深夜3点 http://blog.csdn.net//eastmount/ )
很长时间没有做Android相关知识了,闲暇之余再弄了弄最新的百度地图API,主要是进行百度地图附近餐馆查找功能来练练手,同时熟悉下最新的API教程。文章比较基础,也希望对你有所帮助~参考前文: [android] 百度地图开发 (一).申请AK显示地图及解决显示空白网格问题 [android] 百度地图开发 (二).定位城市位置和城市POI搜索 [android] 百度地图开发 (三).定位当前位置及getLastKnownLocation总为空问题 官方网址:http://developer.baidu.com/map/ Demo下载地址: 一. 配置百度API环境 获取百度地图API Key前,遇到的第一个问题是“小米手机或豌豆荚不能识别USB插口”。解决方法是安装QuickShortcutMaker软件,输入USB启动计算机USB连接。 参考地址:http://www.miui.com/thread-1733895-1-1.html 百度地图AndroidSDK介绍如下: http://developer.baidu.com/map/wiki/index.php?title=androidsdk 第一步:创建百度地图API Key 在使用之前,您需要先申请密钥,且密钥和应用证书和包名绑定。 申请应用新地址:http://lbsyun.baidu.com/apiconsole/key 百度地图Android SDK申请密钥Key方法如下,也可参照我以前写过的一篇文章: http://developer.baidu.com/map/index.php?title=androidsdk/guide/key http://blog.csdn.net/eastmount/article/details/42064123 其中数字签名SHA1通过点击Eclipse的 "窗体(Window)->首选项(Preferences)->Android->Build" 查看,而包名是创建工程BaiduMapRes项目中的包。 第二步:下载Android SDK并配置 Android SDK v3.1.0下载地址: http://developer.baidu.com/map/sdkandev-download.htm 最新下载地址,可以选择自己喜欢的开发资源,它会提供相应的文件包: http://lbsyun.baidu.com/sdk/download 在工程里新建libs文件夹,将开发包里的baidumapapi_vX_X_X.jar拷贝到libs根目录下,将libBaiduMapSDK_vX_X_X.so拷贝到libs\armeabi目录下,拷贝完成后的工程目录如下图所示: 同时需要导入jar包,右键工程->"Properties(属性)"->"Java构建路径",选择添加JAR,选定baidumapapi_vX_X_X.jar,确定后返回。二. Hello BaiduMapAndroid SDK 第一个百度地图程序参考网址,简单进行叙述下:Hello BaiduMapAndroid 第一步:在AndroidManifest中添加开发密钥、所需权限等信息 在application中添加开发密钥 <application><meta-data /></application> 并添加相关权限。<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.baidumapres" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> <!-- 获取网络状态 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 访问网络 --> <uses-permission android:name="android.permission.INTERNET" /> <!-- 获取WiFi状态 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 允许程序写入外部存储,如SD卡上写文件 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <!-- 读取电话状态 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <!-- 获取精确位置 GPS芯片接收卫星的定位信息,定位精度达10米以内 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 通过WiFi或移动基站的方式获取用户错略的经纬度信息 --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 获取模拟定位信息 --> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_GPS" /> <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.GET_TASKS" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="OYl0tcWvGrWtWucQN2rhdxG8" /> <activity android:name="com.example.baidumapres.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 第二步:在布局xml文件中添加地图控件 布局文件activity_main.xml中通过加载百度地图控件MapView,它是由百度提供的自定义控件故需要加上完整包名,同时该控件需要接受点击事件clickable设置为true。<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.baidumapres.MainActivity" tools:ignore="MergeRootFrame" > <com.baidu.mapapi.map.MapView android:id="@+id/map_view" android:layout_width="match_parent" android:layout_height="match_parent" android:clickable="true" /> </FrameLayout> 第三步:MainActivity.java文件,创建地图Activity,管理地图生命周期package com.example.baidumapres; import java.util.ArrayList; import java.util.List; import com.baidu.mapapi.SDKInitializer; import com.baidu.mapapi.map.BaiduMap; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.OverlayOptions; import com.baidu.mapapi.map.PolygonOptions; import com.baidu.mapapi.map.Stroke; import com.baidu.mapapi.model.LatLng; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { //地图 private MapView mMapView = null; private BaiduMap mBaiduMap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //在使用SDK各组件之前初始化context信息,传入ApplicationContext //注意该方法要再setContentView方法之前实现 SDKInitializer.initialize(getApplicationContext()); setContentView(R.layout.activity_main); //获取地图控件引用 mMapView = (MapView) findViewById(R.id.map_view); /* * 设置地图类型 普通地图 * 卫星地图 * mBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE); * 开启交通图 * mBaiduMap.setTrafficEnabled(true); */ mBaiduMap = mMapView.getMap(); mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL); //几何图形覆盖物 //定义多边形的五个顶点 LatLng pt1 = new LatLng(39.93923, 116.357428); LatLng pt2 = new LatLng(39.91923, 116.327428); LatLng pt3 = new LatLng(39.89923, 116.347428); LatLng pt4 = new LatLng(39.89923, 116.367428); LatLng pt5 = new LatLng(39.91923, 116.387428); List<LatLng> pts = new ArrayList<LatLng>(); pts.add(pt1); pts.add(pt2); pts.add(pt3); pts.add(pt4); pts.add(pt5); //构建用户绘制多边形的Option对象 OverlayOptions polygonOption = new PolygonOptions() .points(pts) .stroke(new Stroke(5, 0xAA00FF00)) .fillColor(0xAAFFFF00); //在地图上添加多边形Option,用于显示 mBaiduMap.addOverlay(polygonOption); } @Override protected void onDestroy() { super.onDestroy(); //在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理 mMapView.onDestroy(); } @Override protected void onResume() { super.onResume(); //在activity执行onResume时执行mMapView. onResume (),实现地图生命周期管理 mMapView.onResume(); } @Override protected void onPause() { super.onPause(); //在activity执行onPause时执行mMapView. onPause (),实现地图生命周期管理 mMapView.onPause(); } } 运行该代码简单的测试结果如下所示:包括卫星地图、交通地图、普通地图和五边形覆盖物,其他覆盖物参考官方文档。 三. 百度地图位置定位 基础地图功能介绍参考:http://developer.baidu.com/map/index.php?title=androidsdk/guide/basicmap 类方法介绍:http://wiki.lbsyun.baidu.com/cms/androidsdk/doc/v3_6_1/ 例如参考下图:MapStatusUpdateFactory方法 其中百度地图定位主要参考我以前的代码和crazy1235(推荐大家学习)的文章: [android] 百度地图开发 (三).定位当前位置及getLastKnownLocation总为空问题 百度地图开发(二)之添加覆盖物 + 地理编码和反地理编码 百度地图开发(三)之地图控制 + 定位 - crazy1235 需要注意的地方和核心步骤包括: 1.需要添加定位SDK包,在libs中添加locSDK_3.1.jar和liblocSDK3.so; 2.在AndroidManifest.xml文件中设置service:<application> <meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="OYl0tcWvGrWtWucQN2rhdxG8" /> <activity>....</activity> <service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" > </service> </application> 3.代码中通过BDLocationListener实现定位监听,具体代码如下:package com.example.baidumapres; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import com.baidu.location.LocationClientOption; import com.baidu.mapapi.SDKInitializer; import com.baidu.mapapi.map.BaiduMap; import com.baidu.mapapi.map.BitmapDescriptor; import com.baidu.mapapi.map.BitmapDescriptorFactory; import com.baidu.mapapi.map.CircleOptions; import com.baidu.mapapi.map.MapStatusUpdate; import com.baidu.mapapi.map.MapStatusUpdateFactory; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.MarkerOptions; import com.baidu.mapapi.map.MyLocationConfiguration; import com.baidu.mapapi.map.MyLocationConfiguration.LocationMode; import com.baidu.mapapi.map.MyLocationData; import com.baidu.mapapi.map.OverlayOptions; import com.baidu.mapapi.map.Stroke; import com.baidu.mapapi.model.LatLng; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { //百度地图控件 private MapView mMapView = null; //百度地图对象 private BaiduMap mBaiduMap; //按钮 添加覆盖物 private Button addOverlayBtn; //是否显示覆盖物 1-显示 0-不显示 private int isShowOverlay = 1; //按钮 定位当前位置 private Button locCurplaceBtn; //是否首次定位 private boolean isFirstLoc = true; //定位SDK的核心类 private LocationClient mLocClient; //定位图层显示模式 (普通-跟随-罗盘) private LocationMode mCurrentMode; //定位图标描述 private BitmapDescriptor mCurrentMarker = null; //当前位置经纬度 private double latitude; private double longitude; //定位SDK监听函数 public MyLocationListenner locListener = new MyLocationListenner(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //在使用SDK各组件之前初始化context信息,传入ApplicationContext //注意该方法要再setContentView方法之前实现 SDKInitializer.initialize(getApplicationContext()); setContentView(R.layout.activity_main); //获取地图控件 mMapView = (MapView) findViewById(R.id.map_view); addOverlayBtn = (Button) findViewById(R.id.btn_add_overlay); locCurplaceBtn = (Button) findViewById(R.id.btn_cur_place); addOverlayBtn.setEnabled(false); //设置地图缩放级别16 类型普通地图 mBaiduMap = mMapView.getMap(); MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(16.0f); mBaiduMap.setMapStatus(msu); mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL); //开启定位图层 mBaiduMap.setMyLocationEnabled(true); //定位初始化 //注意: 实例化定位服务 LocationClient类必须在主线程中声明 并注册定位监听接口 mLocClient = new LocationClient(this); mLocClient.registerLocationListener(locListener); LocationClientOption option = new LocationClientOption(); option.setOpenGps(true); //打开GPS option.setCoorType("bd09ll"); //设置坐标类型 option.setScanSpan(5000); //设置发起定位请求的间隔时间为5000ms mLocClient.setLocOption(option); //设置定位参数 mLocClient.start(); //调用此方法开始定位 //Button 添加覆盖物 addOverlayBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { addCircleOverlay(); } }); //Button 定位当前位置 locCurplaceBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { addMyLocation(); } }); } /** * 定位SDK监听器 需添加locSDK jar和so文件 */ public class MyLocationListenner implements BDLocationListener { @Override public void onReceivePoi(BDLocation location) { } @Override public void onReceiveLocation(BDLocation location) { //mapview 销毁后不在处理新接收的位置 if (location == null || mBaiduMap == null) { return; } //MyLocationData.Builder定位数据建造器 MyLocationData locData = new MyLocationData.Builder() .accuracy(location.getRadius()) .direction(100) .latitude(location.getLatitude()) .longitude(location.getLongitude()) .build(); //设置定位数据 mBaiduMap.setMyLocationData(locData); mCurrentMode = LocationMode.NORMAL; //获取经纬度 latitude = location.getLatitude(); longitude = location.getLongitude(); //Toast.makeText(getApplicationContext(), String.valueOf(latitude), Toast.LENGTH_SHORT).show(); //第一次定位的时候,那地图中心点显示为定位到的位置 if (isFirstLoc) { isFirstLoc = false; //地理坐标基本数据结构 LatLng loc = new LatLng(location.getLatitude(),location.getLongitude()); //MapStatusUpdate描述地图将要发生的变化 //MapStatusUpdateFactory生成地图将要反生的变化 MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(loc); mBaiduMap.animateMapStatus(msu); Toast.makeText(getApplicationContext(), location.getAddrStr(), Toast.LENGTH_SHORT).show(); } } } /** * 定位并添加标注 */ private void addMyLocation() { //更新 mBaiduMap.setMyLocationConfigeration(new MyLocationConfiguration( mCurrentMode, true, mCurrentMarker)); mBaiduMap.clear(); addOverlayBtn.setEnabled(true); //定义Maker坐标点 LatLng point = new LatLng(latitude, longitude); //构建Marker图标 BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.drawable.icon_marka); //构建MarkerOption,用于在地图上添加Marker OverlayOptions option = new MarkerOptions() .position(point) .icon(bitmap); //在地图上添加Marker,并显示 mBaiduMap.addOverlay(option); } /** * 添加覆盖物 */ private void addCircleOverlay() { if(isShowOverlay == 1) { //点击显示 mBaiduMap.clear(); isShowOverlay = 0; //DotOptions 圆点覆盖物 LatLng pt = new LatLng(latitude, longitude); CircleOptions circleOptions = new CircleOptions(); //circleOptions.center(new LatLng(latitude, longitude)); circleOptions.center(pt); //设置圆心坐标 circleOptions.fillColor(0xAAFFFF00); //圆填充颜色 circleOptions.radius(250); //设置半径 circleOptions.stroke(new Stroke(5, 0xAA00FF00)); // 设置边框 mBaiduMap.addOverlay(circleOptions); } else { mBaiduMap.clear(); isShowOverlay = 1; } } @Override protected void onDestroy() { mLocClient.stop(); //退出时销毁定位 mBaiduMap.setMyLocationEnabled(false); //关闭定位图层 mMapView.onDestroy(); mMapView = null; super.onDestroy(); } @Override protected void onResume() { super.onResume(); mMapView.onResume(); } @Override protected void onPause() { super.onPause(); mMapView.onPause(); } } 运行结果如下图所示"我的位置"和"添加覆盖物": 四. 百度地图POI附近餐馆查找 PS:这部分下一篇文章将详细介绍,主要是POI附件餐馆和路线规划。五. 新年祝福 最后分享自己圣诞节大学面试和新年祝福的qq说说,与君卿共勉~ 《你明明可以靠才华吃饭,却偏偏要去拼颜值》 很幸运!第一次坐飞机,是为了回家那边的大学去讲课和面试;第一次穿西装,白衬衣里套了三件衣服,其中一件毛衣,别冷着自己就行;第一份圣诞老人的礼物,居然是一份讲台前当老师的承诺。 来之前,一位朋友曾问过我:“你明明可以靠才华吃饭,却偏偏要去拼颜值教书吗?”我的回答是:“我有颜值吗?哈哈,其实教书也能展现我的才华与魅力的啊!” 的确,我也可以去到一个公司,扎扎实实写代码,一个月一万四左右;也有很多创业公司找我做搜索图谱、图像处理、python、selenium。但我更期待自己在讲台前和科研实验室的情景,画面太美,不敢想。 十年后,我不知道自己会不会像春哥的老师一样,也写一篇《别了,这狗日的科研》而放弃大学?但此时此刻还是非常乐观的,至少除了科研教学外,我还会写点代码,这就饿不着;还可以写点文章博客和讲课吧。很多时候,想是一回事,说是一回事,做又是另外一回事。一辈子,又能做多少自己喜欢的事情呢?对吧! 刚刚找工作的那段时间,我看到了一句话:千里马若有人赏识,不论对错,不为输赢。我那时也希望我为一家IT公司奉献很多年,赚个几十万。但后来,我才发现最欣赏我这匹“蠢马”的伯乐,应该在大学,或许就是芸芸众生中的学子一员,或许是那个面试的领导(虽然他一直想不明白一个搞软件的男生为什么要来贵州这边的大学),再或许就是我自己的孤芳自赏吧! 《低俗小说》里面好像有这样一句话:Pride only hurts, it never helps. 有时候自尊心只会让你受伤,其他毫无用处。希望自己在大学能抹去那份傲娇,跟着心走,十年如一日的去坚持那份热情吧!毕竟短暂的激情是不值钱的,只有长久的激情才值钱。 哎!讲完课的当时,还是有一点低落,工资现在只有开发的一个零头了。痛,所以我这么多搞开发的小伙们,以后来找你们玩的时候,懂吧!哈哈,开个玩笑~这里的工作环境和方式我真心喜欢,而且又能教书! 最后用自己的博客签名结束这篇文章吧!“无知·乐观·低调·谦逊·生活“。时刻告诉自己:无知的我需要乐观的去求知,低调的底色是谦逊,而谦逊是源于对生活的通透,我们不止有工作、学习、编程,还要学会享受生活,人生何必走得这么匆忙,开心就好!fighting 新的一年自己最大的愿望就是希望成为一名大学老师,并且身体健康就好! (By:Eastmount 2016-01-06 夜3点 http://blog.csdn.net/eastmount/)
这篇文章主要讲述Python如何安装Numpy、Scipy、Matlotlib、Scikit-learn等库的过程及遇到的问题解决方法。最近安装这个真是一把泪啊,各种不兼容问题和报错,希望文章对你有所帮助吧!你可能遇到的问题包括: ImportError: No module named sklearn 未安装sklearn包 ImportError: DLL load failed: 找不到指定的模块 ImportError: DLL load failed: The specified module could not be found Microsoft Visual C++ 9.0 is required Unable to find vcvarsall.bat Numpy Install RuntimeError: Broken toolchain: cannot link a simple C program ImportError: numpy.core.multiarray failed to import ImportError: cannot import name __check_build ImportError: No module named matplotlib.pyplot 一. 安装过程 最早我是使用"pip install scikit-learn"命令安装的Scikit-Learn程序,并没有注意需要安装Numpy、Scipy、Matlotlib,然后在报错"No module named Numpy"后,我接着使用PIP或者下载exe程序安装相应的包,同时也不理解安装顺序和版本的重要性。其中最终都会报错" ImportError: DLL load failed: 找不到指定的模块",此时我的解决方法是: 错误:sklearn ImportError: DLL load failed: 找不到指定的模块 重点:安装python第三方库时总会出现各种兼容问题,应该是版本问题,版本需要一致。 下载:http://download.csdn.net/detail/eastmount/9366117第一步:卸载原始版本,包括Numpy、Scipy、Matlotlib、Scikit-Learn pip uninstall scikit-learn pip uninstall numpy pip uninstall scipy pip uninstall matplotlib第二步:不使用"pip install package"或"easy_install package"安装,或者去百度\CSDN下载exe文件,而是去到官网下载相应版本。 http://www.lfd.uci.edu/~gohlke/pythonlibs/#scipy http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy http://www.lfd.uci.edu/~gohlke/pythonlibs/#matplotlib http://www.lfd.uci.edu/~gohlke/pythonlibs/#scikit-learn安装过程中最重要的地方就是版本需要兼容。其中操作系统为64位,Python为2.7.8 64位,下载的四个whl文件如下,其中cp27表示CPython 2.7版本,cp34表示CPython 3.4,win_arm64指的是64位版本。 numpy-1.10.2-cp27-none-win_amd64.whl scipy-0.16.1-cp27-none-win_amd64.whl matplotlib-1.5.0-cp27-none-win_amd64.whl scikit_learn-0.17-cp27-none-win_amd64.whl PS:不推荐使用"pip install numpy"安装或下载如"numpy-MKL-1.8.0.win-amd64-py2.7.exe"类似文件,地址如: http://sourceforge.net/projects/numpy/files/NumPy http://sourceforge.net/projects/scipy/files/Scipy 第三步:去到Python安装Scripts目录下,再使用pip install xxx.whl安装,先装Numpy\Scipy\Matlotlib包,再安装Scikit-Learn。 其中我的python安装路径"G:\software\Program software\Python\python insert\Scripts",同时四个whl文件安装核心代码: pip install G:\numpy+scipy+matplotlib\numpy-1.10.2-cp27-none-win_amd64.whl pip install G:\numpy+scipy+matplotlib\scikit_learn-0.17-cp27-none-win_amd64.whl C:\>G: G:\>cd G:\software\Program software\Python\python insert\Scripts G:\software\Program software\Python\python insert\Scripts>pip install G:\numpy+s cipy+matplotlib\numpy-1.10.2-cp27-none-win_amd64.whl Processing g:\numpy+scipy+matplotlib\numpy-1.10.2-cp27-none-win_amd64.whl Installing collected packages: numpy Successfully installed numpy-1.10.2 G:\software\Program software\Python\python insert\Scripts>pip install G:\numpy+s cipy+matplotlib\matplotlib-1.5.0-cp27-none-win_amd64.whl Installing collected packages: matplotlib Successfully installed matplotlib-1.5.0 G:\software\Program software\Python\python insert\Scripts>pip install G:\numpy+s cipy+matplotlib\scipy-0.16.1-cp27-none-win_amd64.whl Processing g:\numpy+scipy+matplotlib\scipy-0.16.1-cp27-none-win_amd64.whl Installing collected packages: scipy Successfully installed scipy-0.16.1 G:\software\Program software\Python\python insert\Scripts>pip install G:\numpy+s cipy+matplotlib\scikit_learn-0.17-cp27-none-win_amd64.whl Processing g:\numpy+scipy+matplotlib\scikit_learn-0.17-cp27-none-win_amd64.whl Installing collected packages: scikit-learn Successfully installed scikit-learn-0.17 第四步:此时配置完成,关键是Python64位版本兼容问题和Scripts目录。最后用北邮论坛一个神人的回复结束这个安装过程:“傻孩子,用套件啊,给你介绍一个Anaconda或winpython。只能帮你到这里了! ” 二. 测试运行环境 搞了这么半天,为什么要装这些呢?给几个用例验证它的正确安装和强大吧!Scikit-Learn是基于python的机器学习模块,基于BSD开源许可。Scikit-learn的基本功能主要被分为六个部分,分类,回归,聚类,数据降维,模型选择,数据预处理,具体可以参考官方网站上的文档。NumPy(Numeric Python)系统是Python的一种开源的数值计算扩展,一个用python实现的科学计算包。它提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库。专为进行严格的数字处理而产生。 内容包括:1、一个强大的N维数组对象Array;2、比较成熟的(广播)函数库;3、用于整合C/C++和Fortran代码的工具包;4、实用的线性代数、傅里叶变换和随机数生成函数。numpy和稀疏矩阵运算包scipy配合使用更加方便。SciPy (pronounced "Sigh Pie") 是一个开源的数学、科学和工程计算包。它是一款方便、易于使用、专为科学和工程设计的Python工具包,包括统计、优化、整合、线性代数模块、傅里叶变换、信号和图像处理、常微分方程求解器等等。Matplotlib是一个Python的图形框架,类似于MATLAB和R语言。它是python最著名的绘图库,它提供了一整套和matlab相似的命令API,十分适合交互式地进行制图。而且也可以方便地将它作为绘图控件,嵌入GUI应用程序中。第一个代码:斜线坐标,测试matplotlib import matplotlib import numpy import scipy import matplotlib.pyplot as plt plt.plot([1,2,3]) plt.ylabel('some numbers') plt.show() 运行结果: 第二个代码:桃心程序,测试numpy和matplotlib 代码参考:Windows 下 Python easy_install 的安装 - KingsLanding import numpy as np import matplotlib.pyplot as plt X = np.arange(-5.0, 5.0, 0.1) Y = np.arange(-5.0, 5.0, 0.1) x, y = np.meshgrid(X, Y) f = 17 * x ** 2 - 16 * np.abs(x) * y + 17 * y ** 2 - 225 fig = plt.figure() cs = plt.contour(x, y, f, 0, colors = 'r') plt.show() 运行结果: 第三个程序:显示Matplotlib强大绘图交互功能 代码参考:Python-Matplotlib安装及简单使用 - bery import numpy as np import matplotlib.pyplot as plt N = 5 menMeans = (20, 35, 30, 35, 27) menStd = (2, 3, 4, 1, 2) ind = np.arange(N) # the x locations for the groups width = 0.35 # the width of the bars fig, ax = plt.subplots() rects1 = ax.bar(ind, menMeans, width, color='r', yerr=menStd) womenMeans = (25, 32, 34, 20, 25) womenStd = (3, 5, 2, 3, 3) rects2 = ax.bar(ind+width, womenMeans, width, color='y', yerr=womenStd) # add some ax.set_ylabel('Scores') ax.set_title('Scores by group and gender') ax.set_xticks(ind+width) ax.set_xticklabels( ('G1', 'G2', 'G3', 'G4', 'G5') ) ax.legend( (rects1[0], rects2[0]), ('Men', 'Women') ) def autolabel(rects): # attach some text labels for rect in rects: height = rect.get_height() ax.text(rect.get_x()+rect.get_width()/2., 1.05*height, '%d'%int(height), ha='center', va='bottom') autolabel(rects1) autolabel(rects2) plt.show() 运行结果: PS:如果设置legend没有显示比例图标,则参考下面代码: # coding=utf-8 import numpy as np import matplotlib import scipy import matplotlib.pyplot as plt #设置legend: http://bbs.byr.cn/#!article/Python/7705 #mark样式: http://www.360doc.com/content/14/1026/02/9482_419859060.shtml #国家 融合特征值 x1 = [10, 20, 50, 100, 150, 200, 300] y1 = [0.615, 0.635, 0.67, 0.745, 0.87, 0.975, 0.49] #动物 x2 = [10, 20, 50, 70, 90, 100, 120, 150] y2 = [0.77, 0.62, 0.77, 0.86, 0.87, 0.97, 0.77, 0.47] #人物 x3 = [10, 20, 50, 70, 90, 100, 120, 150] y3 = [0.86, 0.86, 0.92, 0.94, 0.97, 0.97, 0.76, 0.46] #国家 x4 = [10, 20, 50, 70, 90, 100, 120, 150] y4 = [0.86, 0.85, 0.87, 0.88, 0.95, 1.0, 0.8, 0.49] plt.title('Entity alignment result') plt.xlabel('The number of class clusters') plt.ylabel('Similar entity proportion') plot1, = plt.plot(x1, y1, '-p', linewidth=2) plot2, = plt.plot(x2, y2, '-*', linewidth=2) plot3, = plt.plot(x3, y3, '-h', linewidth=2) plot4, = plt.plot(x4, y4, '-d', linewidth=2) plt.xlim(0, 300) plt.ylim(0.4, 1.0) #plot返回的不是matplotlib对象本身,而是一个列表,加个逗号之后就把matplotlib对象从列表里面提取出来 plt.legend( (plot1,plot2,plot3,plot4), ('Spot', 'Animal', 'People', 'Country'), fontsize=10) plt.show() 输出如下图所示: 第四个代码:矩阵数据集,测试sklearn from sklearn import datasets iris = datasets.load_iris() digits = datasets.load_digits() print digits.data 运行结果: 第五个代码:计算TF-IDF词语权重,测试scikit-learn数据分析 参考代码:http://blog.csdn.net/liuxuejiang158blog/article/details/31360765 # coding:utf-8 __author__ = "liuxuejiang" import jieba import jieba.posseg as pseg import os import sys from sklearn import feature_extraction from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizer if __name__ == "__main__": corpus=["我 来到 北京 清华大学", #第一类文本切词后的结果 词之间以空格隔开 "他 来到 了 网易 杭研 大厦", #第二类文本的切词结果 "小明 硕士 毕业 与 中国 科学院", #第三类文本的切词结果 "我 爱 北京 天安门"] #第四类文本的切词结果 #该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频 vectorizer=CountVectorizer() #该类会统计每个词语的tf-idf权值 transformer=TfidfTransformer() #第一个fit_transform是计算tf-idf,第二个fit_transform是将文本转为词频矩阵 tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus)) #获取词袋模型中的所有词语 word=vectorizer.get_feature_names() #将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重 weight=tfidf.toarray() #打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重 for i in range(len(weight)): print u"-------这里输出第",i,u"类文本的词语tf-idf权重------" for j in range(len(word)): print word[j],weight[i][j] 运行结果: 三. 其他错误解决方法 这里虽然讲解几个安装时遇到的其他错误及解决方法,但作者更推荐上面的安装步骤。 在这之前,我反复的安装、卸载、升级包,其中遇到了各种错误,改了又改,百度了又谷歌。常见PIP用法如下: * pip install numpy --安装包numpy * pip uninstall numpy --卸载包numpy * pip show --files PackageName --查看已安装包 * pip list outdated --查看待更新包信息 * pip install --upgrade numpy --升级包 * pip install -U PackageName --升级包 * pip search PackageName --搜索包 * pip help --显示帮助信息 ImportError: numpy.core.multiarray failed to import python安装numpy时出现的错误,这个通过stackoverflow和百度也是需要python版本与numpy版本一致,解决的方法包括"pip install -U numpy"升级或下载指定版本"pip install numpy==1.8"。但这显然还涉及到更多的包,没有前面的卸载下载安装统一版本的whl靠谱。Microsoft Visual C++ 9.0 is required(unable to find vcvarsall.bat) 因为Numpy内部矩阵运算是用C语言实现的,所以需要安装编译工具,这和电脑安装的VC++或VS2012有关,解决方法:如果已安装Visual Studio则添加环境变量VS90COMNTOOLS即可,不同的VS版本对应不同的环境变量值: Visual Studio 2010 (VS10)设置 VS90COMNTOOLS=%VS100COMNTOOLS% Visual Studio 2012 (VS11)设置 VS90COMNTOOLS=%VS110COMNTOOLS% Visual Studio 2013 (VS12)设置 VS90COMNTOOLS=%VS120COMNTOOLS% 但是这并没有解决,另一种方法是下载Micorsoft Visual C++ Compiler for Python 2.7的包。 下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=44266 参考文章:http://www.oschina.net/question/2294527_244245 PS:这些问题基本解决方法使用pip升级、版本一致、重新下载相关版本exe文件再安装。 总之,最后希望文章对你有所帮助!尤其是刚学习Python和机器学习的同学。 写文不易,且看且珍惜! (By:Eastmount 2015-12-17 晚上10点 http://blog.csdn.net//eastmount/ ) 参考文章: [Python] Windows7 x64安装numpy和scipy - delbert [Python] matplotlib在windows下安装 - sina Windows系统下Python与NumPy安装方法 - bubuko scikit learn 安装及注意事项 - wbgxx333 Python包numpy、Matplotlib、SciPy在64位Windows上的安装 windows下安装scikit learn以及python的各种包 python 机器学习的开发环境搭建(numpy,scipy,matplotlib)
前面讲述了很多关于Python爬取本体Ontology、消息盒InfoBox、虎扑图片等例子,同时讲述了VSM向量空间模型的应用。但是由于InfoBox没有前后文和语义概念,所以效果不是很好,这篇文章主要是爬取百度5A景区摘要信息,再利用Jieba分词工具进行中文分词,最后提出文本聚类算法的一些概念知识。 相关文章: [Python爬虫] Selenium获取百度百科旅游景点的InfoBox消息盒 [python爬虫] Selenium定向爬取海量精美图片及搜索引擎杂谈 Python简单实现基于VSM的余弦相似度计算 基于VSM的命名实体识别、歧义消解和指代消解 [python爬虫] Selenium定向爬取PubMed生物医学摘要信息 一. Selenium爬取百度百科摘要 简单给出Selenium爬取百度百科5A级景区的代码: # coding=utf-8 """ Created on 2015-12-10 @author: Eastmount """ import time import re import os import sys import codecs import shutil from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #Open PhantomJS driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe") #driver = webdriver.Firefox() wait = ui.WebDriverWait(driver,10) #Get the Content of 5A tourist spots def getInfobox(entityName, fileName): try: #create paths and txt files print u'文件名称: ', fileName info = codecs.open(fileName, 'w', 'utf-8') #locate input notice: 1.visit url by unicode 2.write files #Error: Message: Element not found in the cache - # Perhaps the page has changed since it was looked up #解决方法: 使用Selenium和Phantomjs print u'实体名称: ', entityName.rstrip('\n') driver.get("http://baike.baidu.com/") elem_inp = driver.find_element_by_xpath("//form[@id='searchForm']/input") elem_inp.send_keys(entityName) elem_inp.send_keys(Keys.RETURN) info.write(entityName.rstrip('\n')+'\r\n') #codecs不支持'\n'换行 #load content 摘要 elem_value = driver.find_elements_by_xpath("//div[@class='lemma-summary']/div") for value in elem_value: print value.text info.writelines(value.text + '\r\n') #爬取文本信息 #爬取所有段落<div class='para'>的内容 class='para-title'为标题 [省略] time.sleep(2) except Exception,e: #'utf8' codec can't decode byte print "Error: ",e finally: print '\n' info.close() #Main function def main(): #By function get information path = "BaiduSpider\\" if os.path.isdir(path): shutil.rmtree(path, True) os.makedirs(path) source = open("Tourist_spots_5A_BD.txt", 'r') num = 1 for entityName in source: entityName = unicode(entityName, "utf-8") if u'故宫' in entityName: #else add a '?' entityName = u'北京故宫' name = "%04d" % num fileName = path + str(name) + ".txt" getInfobox(entityName, fileName) num = num + 1 print 'End Read Files!' source.close() driver.close() if __name__ == '__main__': main() 内容如下图所示,共204个国家5A级景点的摘要信息。这里就不再叙述: 二. Jieba中文分词 Python中分分词工具很多,包括盘古分词、Yaha分词、Jieba分词等。 中文分词库:http://www.oschina.net/project/tag/264/segment 其中它们的基本用法都相差不大,但是Yaha分词不能处理如“黄琉璃瓦顶”或“圜丘坛”等词,所以使用了结巴分词。 1.安装及入门介绍 参考地址:http://www.oschina.net/p/jieba 下载地址:https://pypi.python.org/pypi/jieba/ Python 2.0我推荐使用"pip install jieba"或"easy_install jieba"全自动安装,再通过import jieba来引用(第一次import时需要构建Trie树,需要等待几秒时间)。 安装时如果出现错误"unknown encoding: cp65001",输入"chcp 936"将编码方式由utf-8变为简体中文gbk。 结巴中文分词涉及到的算法包括: (1) 基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG); (2) 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合; (3) 对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法。 结巴中文分词支持的三种分词模式包括: (1) 精确模式:试图将句子最精确地切开,适合文本分析; (2) 全模式:把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义问题; (3) 搜索引擎模式:在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。 同时结巴分词支持繁体分词和自定义字典方法。#encoding=utf-8 import jieba #全模式 text = "我来到北京清华大学" seg_list = jieba.cut(text, cut_all=True) print u"[全模式]: ", "/ ".join(seg_list) #精确模式 seg_list = jieba.cut(text, cut_all=False) print u"[精确模式]: ", "/ ".join(seg_list) #默认是精确模式 seg_list = jieba.cut(text) print u"[默认模式]: ", "/ ".join(seg_list) #新词识别 “杭研”并没有在词典中,但是也被Viterbi算法识别出来了 seg_list = jieba.cut("他来到了网易杭研大厦") print u"[新词识别]: ", "/ ".join(seg_list) #搜索引擎模式 seg_list = jieba.cut_for_search(text) print u"[搜索引擎模式]: ", "/ ".join(seg_list) 输出如下图所示: 代码中函数简单介绍如下: jieba.cut():第一个参数为需要分词的字符串,第二个cut_all控制是否为全模式。 jieba.cut_for_search():仅一个参数,为分词的字符串,该方法适合用于搜索引擎构造倒排索引的分词,粒度比较细。 其中待分词的字符串支持gbk\utf-8\unicode格式。返回的结果是一个可迭代的generator,可使用for循环来获取分词后的每个词语,更推荐使用转换为list列表。 2.添加自定义词典 由于"国家5A级景区"存在很多旅游相关的专有名词,举个例子: [输入文本] 故宫的著名景点包括乾清宫、太和殿和黄琉璃瓦等 [精确模式] 故宫/的/著名景点/包括/乾/清宫/、/太和殿/和/黄/琉璃瓦/等 [全 模 式] 故宫/的/著名/著名景点/景点/包括/乾/清宫/太和/太和殿/和/黄/琉璃/琉璃瓦/等 显然,专有名词"乾清宫"、"太和殿"、"黄琉璃瓦"(假设为一个文物)可能因分词而分开,这也是很多分词工具的又一个缺陷。但是Jieba分词支持开发者使用自定定义的词典,以便包含jieba词库里没有的词语。虽然结巴有新词识别能力,但自行添加新词可以保证更高的正确率,尤其是专有名词。 基本用法:jieba.load_userdict(file_name) #file_name为自定义词典的路径 词典格式和dict.txt一样,一个词占一行;每一行分三部分,一部分为词语,另一部分为词频,最后为词性(可省略,ns为地点名词),用空格隔开。 强烈推荐一篇词性标注文章,链接如下: http://www.hankcs.com/nlp/part-of-speech-tagging.html#encoding=utf-8 import jieba #导入自定义词典 jieba.load_userdict("dict.txt") #全模式 text = "故宫的著名景点包括乾清宫、太和殿和黄琉璃瓦等" seg_list = jieba.cut(text, cut_all=True) print u"[全模式]: ", "/ ".join(seg_list) #精确模式 seg_list = jieba.cut(text, cut_all=False) print u"[精确模式]: ", "/ ".join(seg_list) #搜索引擎模式 seg_list = jieba.cut_for_search(text) print u"[搜索引擎模式]: ", "/ ".join(seg_list) 输出结果如下所示,其中专有名词连在一起,即"乾清宫"和"黄琉璃瓦"。 3.关键词提取 在构建VSM向量空间模型过程或者把文本转换成数学形式计算中,你需要运用到关键词提取的技术,这里就再补充该内容,而其他的如词性标注、并行分词、获取词位置和搜索引擎就不再叙述了。 基本方法:jieba.analyse.extract_tags(sentence, topK) 需要先import jieba.analyse,其中sentence为待提取的文本,topK为返回几个TF/IDF权重最大的关键词,默认值为20。#encoding=utf-8 import jieba import jieba.analyse #导入自定义词典 jieba.load_userdict("dict.txt") #精确模式 text = "故宫的著名景点包括乾清宫、太和殿和午门等。其中乾清宫非常精美,午门是紫禁城的正门,午门居中向阳。" seg_list = jieba.cut(text, cut_all=False) print u"分词结果:" print "/".join(seg_list) #获取关键词 tags = jieba.analyse.extract_tags(text, topK=3) print u"关键词:" print " ".join(tags) 输出结果如下,其中"午门"出现3次、"乾清宫"出现2次、"著名景点"出现1次,按照顺序输出提取的关键词。如果topK=5,则输出:"午门 乾清宫 著名景点 太和殿 向阳"。>>> 分词结果: 故宫/的/著名景点/包括/乾清宫/、/太和殿/和/午门/等/。/其中/乾清宫/非常/精美/,/午门/是/紫禁城/的/正门/,/午门/居中/向阳/。 关键词: 午门 乾清宫 著名景点 >>> 4.对百度百科获取摘要分词 从BaiduSpider文件中读取0001.txt~0204.txt文件,分别进行分词处理再保存。#encoding=utf-8 import sys import re import codecs import os import shutil import jieba import jieba.analyse #导入自定义词典 jieba.load_userdict("dict_baidu.txt") #Read file and cut def read_file_cut(): #create path path = "BaiduSpider\\" respath = "BaiduSpider_Result\\" if os.path.isdir(respath): shutil.rmtree(respath, True) os.makedirs(respath) num = 1 while num<=204: name = "%04d" % num fileName = path + str(name) + ".txt" resName = respath + str(name) + ".txt" source = open(fileName, 'r') if os.path.exists(resName): os.remove(resName) result = codecs.open(resName, 'w', 'utf-8') line = source.readline() line = line.rstrip('\n') while line!="": line = unicode(line, "utf-8") seglist = jieba.cut(line,cut_all=False) #精确模式 output = ' '.join(list(seglist)) #空格拼接 print output result.write(output + '\r\n') line = source.readline() else: print 'End file: ' + str(num) source.close() result.close() num = num + 1 else: print 'End All' #Run function if __name__ == '__main__': read_file_cut() 运行结果如下图所示: 5.去除停用词 在信息检索中,为节省存储空间和提高搜索效率,在处理自然语言数据(或文本)之前或之后会自动过滤掉某些字或词,这些字或词即被称为Stop Words(停用词)。这些停用词都是人工输入、非自动化生成的,生成后的停用词会形成一个停用词表。但是,并没有一个明确的停用词表能够适用于所有的工具。甚至有一些工具是明确地避免使用停用词来支持短语搜索的。[参考百度百科]#encoding=utf-8 import jieba #去除停用词 stopwords = {}.fromkeys(['的', '包括', '等', '是']) text = "故宫的著名景点包括乾清宫、太和殿和午门等。其中乾清宫非常精美,午门是紫禁城的正门。" segs = jieba.cut(text, cut_all=False) final = '' for seg in segs: seg = seg.encode('utf-8') if seg not in stopwords: final += seg print final #输出:故宫著名景点乾清宫、太和殿和午门。其中乾清宫非常精美,午门紫禁城正门。 seg_list = jieba.cut(final, cut_all=False) print "/ ".join(seg_list) #输出:故宫/ 著名景点/ 乾清宫/ 、/ 太和殿/ 和/ 午门/ 。/ 其中/ 乾清宫/ 非常/ 精美/ ,/ 午门/ 紫禁城/ 正门/ 。 三. 基于VSM的文本聚类算法 这部分主要参考2008年上海交通大学姚清坛等《基于向量空间模型的文本聚类算法》的论文,因为我的实体对齐使用InfoBox存在很多问题,发现对齐中会用到文本内容及聚类算法,所以简单讲述下文章一些知识。 文本聚类的主要依据聚类假设是:同类的文档相似度较大,而非同类文档相似度较小。同时使用无监督学习方法,聚类不需要训练过程以及不需要预先对文档手工标注类别,因此具有较高的灵活性和自动化处理能力。主要分为以下部分: (1) 预处理常用方法 文本信息预处理(词性标注、语义标注),构建统计词典,对文本进行词条切分,完成文本信息的分词过程。 (2) 文本信息的特征表示 采用方法包括布尔逻辑型、概率型、混合型和向量空间模型。其中向量空间模型VSM(Vector Space Model)是将文档映射成向量的形式,(T1, T2, ..., Tn)表示文档词条,(W1, W2, ..., Wn)文档词条对应权重。建立文本特征主要用特征项或词条来表示目标文本信息,构造评价函数来表示词条权重,尽最大限度区别不同的文档。 (3) 文本信息特征缩减 VSM文档特征向量维数众多。因此,在文本进行聚类之前,应用文本信息特征集进行缩减,针对每个特征词的权重排序,选取最佳特征,包括TF-IDF。推荐向量稀疏表示方法,提升聚类的效果,其中(D1, D2, ..., Dn)表示权重不为0的特征词条。 (4) 文本聚类 文本内容表示成数学课分析形势后,接下来就是在此数学基础上进行文本聚类。包括基于概率方法和基于距离方法。其中基于概率是利用贝叶斯概率理论,概率分布方式;基于聚类是特征向量表示文档(文档看成一个点),通过计算点之间的距离,包括层次聚类法和平面划分法。 后面我可能也会写具体的Python聚类算法,VSM计算相似度我前面已经讲过。同时,他的实验数据是搜狐中心的10个大类,包括汽车、财经、IT、体育等,而我的数据都是旅游,如何进一步聚类划分,如山川、河流、博物馆等等,这是另一个难点。 最后还是那句话:不论如何,希望文章对你有所帮助,如果文章中有错误或不足之处,还请海涵~写文不易,且看且分析。加油!!! (By:Eastmount 2015-12-11 深夜3点 http://blog.csdn.net/eastmount/)
本文主要针对python使用urlretrieve或urlopen下载百度、搜狗、googto(谷歌镜像)等图片时,出现"无法打开图片或已损坏"的问题,作者对它进行简单的探讨。同时,作者将进一步帮你巩固selenium自动化操作和urllib库等知识。 感谢朋友"露为霜"的帮助!希望以后能实现强大的图片爬虫代码~ 一. 引入Selenium自动爬取百度图片 下面这部分Selenium代码的主要功能是: 1.先自动运行浏览器,并访问百度图片链接:http://image.baidu.com/ 2.通过driver.find_element_by_xpath()函数获取输入框的位置; 3.在输入框中自动输入搜索关键词"邓肯",再输入回车搜索"邓肯"相关图片; 4.再通过find_element_by_xpath()获取图片的原图url,这里仅获取一张图片; 5.调用urllib的urlretrieve()函数下载图片。 最后整个动态效果如下图所示,但是图片却无法显示: 代码如下:# -*- coding: utf-8 -*- import urllib import re import time import os from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #Open PhantomJS #driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe") driver = webdriver.Firefox() wait = ui.WebDriverWait(driver,10) #Search Picture By Baidu url = "http://image.baidu.com/" name = u"邓肯" driver.get(url) elem_inp = driver.find_element_by_xpath("//form[@id='homeSearchForm']/span[1]/input") elem_inp.send_keys(name) elem_inp.send_keys(Keys.RETURN) time.sleep(5) #Get the URL of Pictures #elem_pic = driver.find_element_by_xpath("//div[@class='imgpage']/ul/li/div/a") elem_pic = driver.find_element_by_xpath("//div[@class='imgpage']/ul/li/div/a/img") elem_url = elem_pic.get_attribute("src") print elem_url #Download Pictures driver.get(elem_url) urllib.urlretrieve(elem_url,"picture.jpg") print "Download Pictures!!!" 二. 简单分析原因及知识巩固 1.urllib.urlretrieve() 通过urlretrieve()函数可设置下载进度发现图片是一下子就加载的。这里给大家巩固这个urlretrieve函数的方法和Python时间命名方式,代码如下: # -*- coding: utf-8 -*- import urllib import time import os #显示下载进度 def schedule(a,b,c): #a:已下载的数据块 b:数据块的大小 c:远程文件的大小 per = 100.0 * a * b / c if per > 100 : per = 100 print '%.2f%%' % per if __name__ == '__main__': url = "http://img4.imgtn.bdimg.com/it/u=3459898135,859507693&fm=11&gp=0.jpg" #定义文件名 时间命名 t = time.localtime(time.time()) #反斜杠连接多行 filename = str(t.__getattribute__("tm_year")) + "_" + \ str(t.__getattribute__("tm_mon")) + "_" + \ str(t.__getattribute__("tm_mday")) target = "%s.jpg" % filename print target urllib.urlretrieve(url,target,schedule) print "Download Picture!!!" 发现该图片的大小仅为168字节,其中输出结果如下图,获取的URL地址如下:http://img4.imgtn.bdimg.com/it/u=3459898135,859507693&fm=11&gp=0.jpg 而换张图片是能显示下载进度的,如我的头像。显然我想让程序加个进度就能爬取图片的想法失败。头像地址:http://avatar.csdn.net/F/8/5/1_eastmount.jpg 猜测可能获取的百度URL不是原图地址,或者是个服务器设置了相应的拦截或加密。参考"Python爬虫抓取网页图片",函数相关介绍如下: >>> help(urllib.urlretrieve) Help on function urlretrieve in module urllib: urlretrieve(url, filename=None, reporthook=None, data=None) 参数url: 指定的下载路径 参数 finename: 指定了保存本地路径(如果参数未指定,urllib会生成一个临时文件保存数据。) 参数 reporthook: 是一个回调函数,当连接上服务器、以及相应的数据块传输完毕时会触发该回调, 我们可以利用这个回调函数来显示当前的下载进度。 参数 data: 指 post 到服务器的数据,该方法返回一个包含两个元素的(filename, headers)元组, filename 表示保存到本地的路径,header 表示服务器的响应头。 2.urllib2.urlopen() 换个方法urlopen()实现,同时设置消息头试试,并输出信息和图片大小。 # -*- coding: utf-8 -*- import os import sys import urllib import urllib2 #设置消息头 url = "http://img4.imgtn.bdimg.com/it/u=3459898135,859507693&fm=11&gp=0.jpg" header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) \ AppleWebKit/537.36 (KHTML, like Gecko) \ Chrome/35.0.1916.114 Safari/537.36', 'Cookie': 'AspxAutoDetectCookieSupport=1' } request = urllib2.Request(url, None, header) response = urllib2.urlopen(request) print response.headers['Content-Length'] with open("picture.jpg","wb") as f: f.write(response.read()) print response.geturl() print response.info() #返回报文头信息 print urllib2.urlopen(url).read() 返回内容是”HTTPError: HTTP Error 403: Forbidden“,Selenium打开如下: 其中403错误介绍如下,服务器拒绝服务: 换成我的博客图像那张图是能下载的,同时设置消息头和代理,推荐一篇文章: [Python]网络爬虫(五):urllib2的使用细节与抓站技巧 三. 解决方法 主要参考三篇文章和自己的一些想法: selenium+python 爬取网络图片(2) -- 百度 Python 3 多线程下载百度图片搜索结果 CSDN博客搬家到WordPress - curl设置headers爬取 第一个方法 F12审查元素和SRC的骗局 这是感谢"露为霜"同学提供的方法,如果你通过浏览器点开百度搜索"邓肯"的第一张图片,复制网址后,会发现图片真实的地址为: http://gb.cri.cn/mmsource/images/2015/11/22/sb2015112200073.jpg 此时你再分析百度搜索页面,你会发现"F12审查元素和获取src元素的行为欺骗了你",正是因为它俩定位到了错误的图片链接。而真实的URL是在"ul/li/"中的"data-objurl"属性中。 代码如下: # -*- coding: utf-8 -*- import urllib import re import time import os from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #Open PhantomJS #driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe") driver = webdriver.Firefox() wait = ui.WebDriverWait(driver,10) #Search Picture By Baidu url = "http://image.baidu.com/" name = u"邓肯" driver.get(url) elem_inp = driver.find_element_by_xpath("//form[@id='homeSearchForm']/span[1]/input") elem_inp.send_keys(name) elem_inp.send_keys(Keys.RETURN) time.sleep(5) #Get the URL of Pictures num = 1 elem_pic = driver.find_elements_by_xpath("//div[@class='imgpage']/ul/li") for elem in elem_pic: elem_url = elem.get_attribute("data-objurl") print elem_url #Download Pictures name = "%03d" % num urllib.urlretrieve(elem_url, str(name) + ".jpg") num = num + 1 else: print "Download Pictures!!!" 运行代码成功爬取了9张图片,显然成功了!虽然最后报错:IOError: [Errno socket error] [Errno 10060] ,只爬取了9张图片,但是至少可以正确解决了该问题。运行截图如下所示: 同样的道理,googto的elem.get_attribute("src")改成elem.get_attribute("data-imgurl")即可获取正确的图片地址并正确下载。 PS:百度图片动态加载的功能是非常强大的,当你的鼠标拖动时,它会自动增加新的页面,在<ul>中包括新的一批<li>张图片,这也是不同于其它网页在右下角点击"1、2、3..."翻页的,可能也会成为海量图片爬取的又一难点。 第二个方法 Selenium使用右键另存为 还是使用老的链接,虽然读取是无法显示的,但尝试通过Selenium的鼠标右键另存为功能,看能不能爬取成功。# -*- coding: utf-8 -*- import urllib import re import time import os from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #Open PhantomJS driver = webdriver.Firefox() wait = ui.WebDriverWait(driver,10) #Search Picture By Baidu url = "http://image.baidu.com/" name = u"邓肯" driver.get(url) elem_inp = driver.find_element_by_xpath("//form[@id='homeSearchForm']/span[1]/input") elem_inp.send_keys(name) elem_inp.send_keys(Keys.RETURN) time.sleep(5) #Get the URL of Pictures elem_pic = driver.find_element_by_xpath("//div[@class='imgpage']/ul/li/div/a/img") elem_url = elem_pic.get_attribute("src") print elem_url #鼠标移动至图片上 右键保存图片 driver.get(elem_url) print driver.page_source elem = driver.find_element_by_xpath("//img") action = ActionChains(driver).move_to_element(elem) action.context_click(elem) #右键 #当右键鼠标点击键盘光标向下则移动至右键菜单第一个选项 action.send_keys(Keys.ARROW_DOWN) action.send_keys('v') #另存为 action.perform() print "Download Pictures!!!" 运行效果如下图所示。虽然它能实现右键另存为,但是需要手动点击保存,其原因是selenium无法操作操作系统级的对话框,又说"set profile"代码段的设置能解决问题的并不靠谱。通过钩子Hook函数可以实现,以前做过C#的钩子自动点击功能,但是想到下载图片需要弹出并点击无数次对话框就很蛋疼,所以该方法并不好! 钩子函数java版本结合robot可以阅读下面这篇文章: selenium webdriver 右键另存为下载文件(结合robot and autoIt) 第三个方法 通过Selenium自动点击百度的下载按钮 其实现过程就是通过Selenium找到"下载"按钮,再点击或获取链接即可。 该方法参考文章:selenium+python 爬取网络图片(2) -- 百度 同时,这里需要强调百度动态加载,可以通过Selenium模拟滚动窗口实现,也参考上面文章。其中核心代码为: driver.maximize_window() pos += i*500 # 每次下滚500 js = "document.documentElement.scrollTop=%d" % pos driver.execute_script(js) 第四个方法 百度图片解码下载及线程实现 参考文章:Python 3 多线程下载百度图片搜索结果 最近看了一些优秀的文章,真心感觉自己缕蚁一般,太过渺小,还有好多知识需要学习啊!加油~而且不知道现在自己做的这些东西是否有用?心理的几个想法一直还未实现,挺担心的。还是自己博客描述那句话: 无知的自己 · 乐观的面对 · 谦逊的学习 · 低调的前行 · 更要会生活 希望文章对你有所帮助,如果有错误或不足之处,还请海涵~ (By:Eastmount 2015-12-07 清晨6点 http://blog.csdn.net/eastmount/)
本文主要是自己的在线代码笔记。在生物医学本体Ontology构建过程中,我使用Selenium定向爬取生物医学PubMed数据库的内容。 PubMed是一个免费的搜寻引擎,提供生物医学方面的论文搜寻以及摘要。它的数据库来源为MEDLINE(生物医学数据库),其核心主题为医学,但亦包括其他与医学相关的领域,像是护理学或者其他健康学科。它同时也提供对于相关生物医学资讯上相当全面的支援,像是生化学与细胞生物学。 PubMed是因特网上使用最广泛的免费MEDLINE,该搜寻引擎是由美国国立医学图书馆提供,它是基于WEB的生物医学信息检索系统,它是NCBI Entrez整个数据库查询系统中的一个。PubMed界面提供与综合分子生物学数据库的链接,其内容包括:DNA与蛋白质序列,基因图数据,3D蛋白构象,人类孟德尔遗传在线,也包含着与提供期刊全文的出版商网址的链接等。 医学导航链接:http://www.meddir.cn/cate/736.htm PubMed官网:http://pubmed.cn/ 实现代码 实现的代码主要是Selenium通过分析网页DOM结点进行爬取。 爬取的地址是:http://www.medlive.cn/pubmed/ 在网址中搜索Protein(蛋白质)后,分析网址可发现设置Page=1~20可爬取前1~20页的URL信息。链接如下: http://www.medlive.cn/pubmed/pubmed_search.do?q=protein&page=1 # coding=utf-8 """ Created on 2015-12-05 Ontology Spider @author Eastmount CSDN URL: http://www.meddir.cn/cate/736.htm http://www.medlive.cn/pubmed/ http://paper.medlive.cn/literature/1502224 """ import time import re import os import shutil import sys import codecs from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #Open PhantomJS driver = webdriver.Firefox() driver2 = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe") wait = ui.WebDriverWait(driver,10) ''' Load Ontoloty 去到每个生物本体页面下载摘要信息 http://paper.medlive.cn/literature/literature_view.php?pmid=26637181 http://paper.medlive.cn/literature/1526876 ''' def getAbstract(num,title,url): try: fileName = "E:\\PubMedSpider\\" + str(num) + ".txt" #result = open(fileName,"w") #Error: 'ascii' codec can't encode character u'\u223c' result = codecs.open(fileName,'w','utf-8') result.write("[Title]\r\n") result.write(title+"\r\n\r\n") result.write("[Astract]\r\n") driver2.get(url) elem = driver2.find_element_by_xpath("//div[@class='txt']/p") #print elem.text result.write(elem.text+"\r\n") except Exception,e: print 'Error:',e finally: result.close() print 'END\n' ''' 循环获取搜索页面的URL 规律 http://www.medlive.cn/pubmed/pubmed_search.do?q=protein&page=1 ''' def getURL(): page = 1 #跳转的页面总数 count = 1 #统计所有搜索的生物本体个数 while page<=20: url_page = "http://www.medlive.cn/pubmed/pubmed_search.do?q=protein&page="+str(page) print url_page driver.get(url_page) elem_url = driver.find_elements_by_xpath("//div[@id='div_data']/div/div/h3/a") for url in elem_url: num = "%05d" % count title = url.text url_content = url.get_attribute("href") print num print title print url_content #自定义函数获取内容 getAbstract(num,title,url_content) count = count + 1 else: print "Over Page " + str(page) + "\n\n" page = page + 1 else: "Over getUrl()\n" time.sleep(5) ''' 主函数预先运行 ''' if __name__ == '__main__': ''' path = "F:\\MedSpider\\" if os.path.isfile(path): #Delete file os.remove(path) elif os.path.isdir(path): #Delete dir shutil.rmtree(path, True) os.makedirs(path) #Create the file directory ''' getURL() print "Download has finished." 分析HTML 1.获取每页Page中的20个关于Protein(蛋白质)的URL链接和标题。其中getURL()函数中的核心代码获取URL如下: url = driver.find_elements_by_xpath("//div[@id='div_data']/div/div/h3/a") url_content = url.get_attribute("href") getAbstract(num,title,url_content) 2.再去到具体的生物文章页面获取摘要信息 其中你可能遇到的错误包括: 1.Error: 'ascii' codec can't encode character u'\u223c' 它是文件读写编码错误,我通常会将open(fileName,"w")改为codecs.open(fileName,'w','utf-8') 即可。 2.第二个错误如下图所示或如下,可能是因为网页加载或Connection返回Close导致 WebDriverException: Message: Error Message => 'URL ' didn't load. Error: 'TypeError: 'null' is not an object 运行结果 得到的运行结果如下所示:00001.txt~00400.txt共400个txt文件,每个文件包含标题和摘要,该数据集可简单用于生物医学的本体学习、命名实体识别、本体对齐构建等。 PS:最后也希望这篇文章对你有所帮助吧!虽然文章内容很简单,但是对于初学者或者刚接触爬虫的同学来说,还是有一定帮助的。同时,这篇文章更多的是我的个人在线笔记,简单记录下一段代码,以后也不会再写Selenium这种简单的爬取页面的文章了,更多是一些智能动态的操作和Scrapy、Python分布式爬虫的文章吧。如果文中有错误和不足之处,还请海涵~昨天自己生日,祝福自己,老师梦啊老师梦!!! (By:Eastmount 2015-12-06 深夜3点半 http://blog.csdn.net/eastmount/)
前面介绍了很多Selenium基于自动测试的Python爬虫程序,主要利用它的xpath语句,通过分析网页DOM树结构进行爬取内容,同时可以结合Phantomjs模拟浏览器进行鼠标或键盘操作。但是,更为广泛使用的Python爬虫框架是——Scrapy爬虫。这是一篇在Windows系统下介绍 Scrapy爬虫安装及入门介绍的相关文章。 官方 Scrapy :http://scrapy.org/ 官方英文文档:http://doc.scrapy.org/en/latest/index.html 官方中文文档:https://scrapy-chs.readthedocs.org/zh_CN/0.24/index.html 一. 安装过程 本文主要讲述Windows下的安装过程,首先我的Python是2.7.8版本。 主要通过Python的PIP语句进行安装: pip install scrapy 安装PIP参考:http://blog.csdn.net/eastmount/article/details/47785123 通过 pip list outdated 命令查看软件最新版本,表示PIP安装成功。 然后,输入 pip install scrapy 命令进行安装。 安装成功后,通过cmd调用 scrapy 指令查看,表示安装成功。 如果过程中存在如下图所示错误"no module named win32api",则需要下载安装win32,选择2.7.8版本。地址为:http://sourceforge.net/projects/pywin32/files/ 此时,scrapy安装成功,可以进行第二步"第一个scrapy爬虫实现"了~ 正如xifeijian大神所说:“作为Python爱好者,如果不知道easy_install或者pip中的任何一个的话,那么......”。easy_insall的作用和perl中的cpan,ruby中的gem类似,都提供了在线一键安装模块的傻瓜方便方式,而pip是easy_install的改进版,提供更好的提示信息,删除package等功能。老版本的python中只有easy_install,没有pip。常见的具体用法如下:easy_install的用法: 1) 安装一个包 $ easy_install <package_name> $ easy_install "<package_name>==<version>" 2) 升级一个包 $ easy_install -U "<package_name>>=<version>" pip的用法 1) 安装一个包 $ pip install <package_name> $ pip install <package_name>==<version> 2) 升级一个包 (如果不提供version号,升级到最新版本) $ pip install --upgrade <package_name>>=<version> 3)删除一个包 $ pip uninstall <package_name> 二. 第一个scrapy爬虫程序实现 官网介绍: Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了 页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。 An open source and collaborative framework for extracting the data you need from websites. In a fast, simple, yet extensible way. 下面是参考官网在windows下实现的第一个scrapy爬虫程序: 打开Python IDLE,创建myspider.py文件,代码如下:import scrapy class BlogSpider(scrapy.Spider): name = 'blogspider' start_urls = ['http://blog.scrapinghub.com'] def parse(self, response): for url in response.css('ul li a::attr("href")').re(r'.*/\d\d\d\d/\d\d/$'): yield scrapy.Request(response.urljoin(url), self.parse_titles) def parse_titles(self, response): for post_title in response.css('div.entries > ul > li a::text').extract(): yield {'title': post_title} 如果此时你试图点击Run运行程序或在IDLE中输入 scrapy runspider myspider.py,似乎返回如下错误: 此时我怀疑Windows下需要调用cmd运行程序,还是强烈推荐在Linux下学习使用Python相关编程知识。调用cd去到文件所在目录: cd G:\software\Program software\Python\python insert\scrapy project 然后在运行程序,结果输出如下所示: 此时,程序能够运行成功了,不论结果如何、代码如何,总算在Windows下跑起来了。下面第三部分,我再简单介绍如何调用Scrapy爬虫进行一个入门相关的爬取~ 三. Scrapy入门介绍 入门介绍参考:初窥Scrapy 和 Scrapy入门教程 给大家简单举个例子,使用maxliaops的Scrapy爬虫爬取腾讯的职位招聘信息。 代码下载:https://github.com/maxliaops/scrapy-itzhaopin 源文链接:http://blog.csdn.net/HanTangSongMing/article/details/24454453 目标网址为:http://hr.tencent.com/position.php Windows下Ctrl+R调用CMD命令行。输入命令如下:1.chcp 936 unknown encoding: cp65001异常时,需要将编码(UTF-8)修改为 简体中文(GBK) 2.cd G:\software\Program software\Python\python insert\scrapy project 去到安装Scrapy目录下 3.cd scrapy-itzhaopin-master\itzhaopin 再次去到下载的文件itzhaopin目录下 4.scrapy crawl tencent 运行代码启动这个Spider,进行下载 最后运行会在scrapy-itzhaopin-master\itzhaopin文件夹下生产一个tencent.json的结果。数据量很大,下图只展示部分日期是2015-11-07的数据,如下所示: 其中代码itzhaopin项目的结果图如下所示:参考原文作者博客 ├── itzhaopin │ ├── itzhaopin │ │ ├── __init__.py │ │ ├── items.py │ │ ├── pipelines.py │ │ ├── settings.py │ │ └── spiders │ │ └── __init__.py │ └── scrapy.cfg scrapy.cfg: 项目配置文件 items.py: 需要提取的数据结构定义文件 pipelines.py:管道定义,用来对items里面提取的数据做进一步处理,如保存等 settings.py: 爬虫配置文件 spiders: 放置spider的目录 核心的几个py文件内容如下,详见github: 1.items.py:定义我们要抓取的数据# Define here the models for your scraped items # # See documentation in: # http://doc.scrapy.org/en/latest/topics/items.html from scrapy.item import Item, Field class TencentItem(Item): name = Field() # 职位名称 catalog = Field() # 职位类别 workLocation = Field() # 工作地点 recruitNumber = Field() # 招聘人数 detailLink = Field() # 职位详情页链接 publishTime = Field() # 发布时间 2.spiders文件夹中tencent_spider.py文件:实现Spider Spider是一个继承自scrapy.contrib.spiders.CrawlSpider的Python类,有三个必需的定义的成员 name: 名字,这个spider的标识 start_urls:一个url列表,spider从这些网页开始抓取 parse():一个方法,当start_urls里面的网页抓取下来之后需要调用这个方法解析网页内容,同时需要返回下一个需要抓取的网页,或者返回items列表import re import json from scrapy.selector import Selector try: from scrapy.spider import Spider except: from scrapy.spider import BaseSpider as Spider from scrapy.utils.response import get_base_url from scrapy.utils.url import urljoin_rfc from scrapy.contrib.spiders import CrawlSpider, Rule from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor as sle from itzhaopin.items import * from itzhaopin.misc.log import * class TencentSpider(CrawlSpider): name = "tencent" allowed_domains = ["tencent.com"] start_urls = [ "http://hr.tencent.com/position.php" ] rules = [ Rule(sle(allow=("/position.php\?&start=\d{,4}#a")), follow=True, callback='parse_item') ] def parse_item(self, response): items = [] sel = Selector(response) base_url = get_base_url(response) sites_even = sel.css('table.tablelist tr.even') for site in sites_even: item = TencentItem() item['name'] = site.css('.l.square a').xpath('text()').extract()[0] relative_url = site.css('.l.square a').xpath('@href').extract()[0] item['detailLink'] = urljoin_rfc(base_url, relative_url) item['catalog'] = site.css('tr > td:nth-child(2)::text').extract()[0] item['workLocation'] = site.css('tr > td:nth-child(4)::text').extract()[0] item['recruitNumber'] = site.css('tr > td:nth-child(3)::text').extract()[0] item['publishTime'] = site.css('tr > td:nth-child(5)::text').extract()[0] items.append(item) #print repr(item).decode("unicode-escape") + '\n' sites_odd = sel.css('table.tablelist tr.odd') for site in sites_odd: item = TencentItem() item['name'] = site.css('.l.square a').xpath('text()').extract()[0] relative_url = site.css('.l.square a').xpath('@href').extract()[0] item['detailLink'] = urljoin_rfc(base_url, relative_url) item['catalog'] = site.css('tr > td:nth-child(2)::text').extract()[0] item['workLocation'] = site.css('tr > td:nth-child(4)::text').extract()[0] item['recruitNumber'] = site.css('tr > td:nth-child(3)::text').extract()[0] item['publishTime'] = site.css('tr > td:nth-child(5)::text').extract()[0] items.append(item) #print repr(item).decode("unicode-escape") + '\n' info('parsed ' + str(response)) return items def _process_request(self, request): info('process ' + str(request)) return request 3.pipelines.py:实现PipeLine PipeLine用来对Spider返回的Item列表进行保存操作,可以写入到文件、或者数据库等。PipeLine只有一个需要实现的方法:process_item,例如我们将Item保存到JSON格式文件中:# Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html from scrapy import signals import json import codecs class JsonWithEncodingTencentPipeline(object): def __init__(self): self.file = codecs.open('tencent.json', 'w', encoding='utf-8') def process_item(self, item, spider): line = json.dumps(dict(item), ensure_ascii=False) + "\n" self.file.write(line) return item def spider_closed(self, spider): self.file.close( ) 4.settings.py:设置文件# Scrapy settings for itzhaopin project # # For simplicity, this file contains only the most important settings by # default. All the other settings are documented here: # # http://doc.scrapy.org/en/latest/topics/settings.html # BOT_NAME = 'itzhaopin' SPIDER_MODULES = ['itzhaopin.spiders'] NEWSPIDER_MODULE = 'itzhaopin.spiders' # Crawl responsibly by identifying yourself (and your website) on the user-agent #USER_AGENT = 'itzhaopin (+http://www.yourdomain.com)' ITEM_PIPELINES = { 'itzhaopin.pipelines.JsonWithEncodingTencentPipeline': 300, } LOG_LEVEL = 'INFO' 看了这个简单的例子后,后面的文章就会根据原文进行一些自定义的爬虫实验了,希望对你有所帮助吧~同时还是感觉Linux下学习这些更适合些。最后推荐两篇文章: Python爬虫框架Scrapy实战之定向批量获取职位招聘信息 Scrapy研究探索专栏 作者:young-hz [Python]网络爬虫(12):爬虫框架Scrapy的第一个爬虫示例入门教程 (By:Eastmount 2015-11-08 深夜4点 http://blog.csdn.net/eastmount/)
最近在做知识图谱实体对齐和属性对齐中,简单用了下Word2vec谷歌开源代码。Word2vec是一个将单词表征成向量的形式,它可以把文本内容的处理简化为向量空间中的向量运算,计算出向量空间上的相似度,来表示文本语义上的相似度。 Word2vec采用CBOW(Continuous Bag-Of-Words Model,连续词袋模型)和Skip-Gram(Continuous Skip-GramModel)两种模型,涉及到神经网络和深度学习的一些知识。故这周给学弟和同学们分享PPT的主题就是《神经网络是个什么东东?》,参考了很多资料并简单讲述了机器学习和神经网络的入门知识。 希望文章对你有所帮助,文章中分享了很多参考资料和最后对吴老的赞扬~ 一. 机器学习入门介绍 以前转载过"数盟"的一篇关于机器学习入门介绍的文章,这里简单引入它的几张图片作为引入。参考原文地址和转载地址: 机器学习科普文章:“一文读懂机器学习,大数据/自然语言处理/算法全有了” http://blog.csdn.net/eastmount/article/details/43673209 机器学习这个词是让人疑惑的,首先它是英文名称Machine Learning(简称ML)的直译,在计算界Machine一般指计算机。这个名字使用了拟人的手法,说明了这门技术是让机器“学习”的技术。但是计算机是死的,怎么可能像人类一样“学习”呢? 下图是非常形象的将机器学习的过程与人类对历史经验归纳的过程做个比对: 机器学习方法是计算机利用已有的数据(经验),得出了某种模型(迟到的规律),并利用此模型预测未来(是否迟到)的一种方法。 常见的例子包括房价与面积、预测患病概率等等,详见斯坦福NG教授课程。 下图是机器学习所牵扯的一些相关范围的学科与研究领域。 简单的等价划分如下: 模式识别=机器学习: 两者的主要区别在于前者是从工业界发展起来的概念,后者则主要源自计算机学科。 数据挖掘=机器学习+数据库: 从数据中挖出金子,以及将废弃的数据转化为价值。 计算机视觉=图像处理+机器学习: 图像处理技术用于将图像处理为适合进入机器学习模型中的输入,机器学习则负责从图像中识别出相关的模式。例如百度识图、手写字符识别、车牌识别等等应用。 统计学习≈机器学习: 统计学习是个与机器学习高度重叠的学科。因为机器学习中的大多数方法来自统计学,甚至可认为统计学的发展促进机器学习的繁荣昌盛。例如著名的支持向量机算法,就是源自统计学科。区别在于统计学习者重点关注的是统计模型的发展与优化,偏数 学,而机器学习者更关注的是能够解决问题,偏实践。 语音识别=语音处理+机器学习: 语音识别就是音频处理技术与机器学习的结合。一般会结合自然语言处理的相关技术,目前的相关应用有苹果的语音助手siri。 自然语言处理=文本处理+机器学习: 让机器理解人类的语言,NLP中大量使用了编译原理相关的技术,例如词法分析、语法分析、语义理解等。 自然语言处理作为唯一由人类自身创造的符号,一直是机器学习界不断 研究的方向。按照百度机器学习专家余凯的说法“听与看,说白了就是阿猫和阿狗都会的,而只有语言才是人类独有的”。如何利用机器学习技术进行自然语言的的深度理解,一直是工业和学术界关注的焦点。 Deep Learning (深度学习)算法已经在图像和音频领域取得了惊人的成果,但是在 NLP 领域中尚未见到如此激动人心的结果。有一种说法是: 语言(词、句子、篇章等)属于人类认知过程中产生的高层认知抽象实体,而语音和图像属于较为底层的原始输入信号,所以后两者更适合做deep learning来学习特征。 而将词用“词向量”的方式表示可谓是将 Deep Learning 算法引入 NLP 领域的一个核心技术。大多数宣称用了 Deep Learning 的论文,其中往往也用了词向量。显然Word2vec也引入了词向量。参考licstar文章: Deep Learning in NLP (一)词向量和语言模型 机器学习的方法很多,其中经典的方法包括:回归算法、神经网络、支持向量机SVM、聚类算法、降维算法、推荐算法、朴素贝叶斯、决策树等。详见这两篇文章: 常见的机器学习&数据挖掘知识点 - 作者:一只鸟的天空 [转载] 机器学习面试之算法思想简单梳理 二. 神经网络入门介绍 该部分主要通过白话文的方式讲述神经网络,其中主要转载吴老的文章。链接: 吴祖增前辈:神经网络入门(连载之一) 吴祖增前辈:神经网络入门(连载之二) 斯坦福机器学习视频NG教授 https://class.coursera.org/ml/class/index 书籍《游戏开发中的人工智能》、《游戏编程中的人工智能技术》 神经网络(也称之为人工神经网络,ANN)算法是80年代机器学习界非常流行的算法,不过在90年代中途衰落。现在,携着“深度学习”之势,神经网络重装归来,重新成为最强大的机器学习算法之一。 人工神经网络(artificial neural network,缩写ANN),是一种模仿生物神经网络的结构和功能的数学模型或计算模型。神经网络由大量的人工神经元联结进行计算。其来源于生物,故吴老先先讲述了生物神经网络的基础知识,从而进行引入。 神经细胞通过轴突将信号传递给其他的神经细胞,通过树突向各个方向接受信号。 神经细胞利用电-化学过程交换信号。输入信号来自另一些神经细胞。这些神经细胞的轴突末梢(也就是终端)和本神经细胞的树突相遇形成突触(synapse),信号就从树突上的突触进入本细胞。 信号在大脑中实际怎样传输是一个相当复杂的过程,但就我们而言,重要的是把它看成和现代的计算机一样,利用一系列的0和1来进行操作。就是说,大脑的神经细胞也只有两种状态:兴奋(fire)和不兴奋(即抑制)。 神经细胞利用一种我们还不知道的方法,把所有从树突突触上进来的信号进行相加,如果全部信号的总和超过某个阀值,就会激发神经细胞进入兴奋(fire)状态,这时就会有一个电信号通过轴突发送出去给其他神经细胞。如果信号总和没有达到阀值,神经细胞就不会兴奋起来。这样的解释有点过分简单化,但已能满足我们的目的。 由于人脑具有一下几个特点: 1.能实现无监督的学习 大脑能够自己进行学习,而不需要导师的监督教导。如果一个神经细胞在一段时间内受到高频率的刺激,则它和输入信号的神经细胞之间的连接强度就会按某种过程改变,使得该神经细胞下一次受到激励时更容易兴奋。 2.对损伤有冗余性(tolerance) 大脑即使有很大一部分受到了损伤, 它仍然能够执行复杂的工作。 3.处理信息的效率极高 神经细胞之间电-化学信号的传递,与一台数字计算机中CPU的数据传输相比,速度是非常慢的,但因神经细胞采用了并行的工作方式,使得大脑能够同时处理大量的数据。例如,大脑视觉皮层在处理通过我们的视网膜输入的一幅图象信号时,大约只要100ms的时间就能完成,眼睛并发执行。 4.善于归纳推广 大脑和数字计算机不同,它极擅长的事情之一就是模式识别,并能根据已熟悉信息进行归纳推广(generlize)。例如,我们能够阅读他人所写的手稿上的文字,即使我们以前从来没见过他所写的东西。 5.它是有意识的 如下图所示,它表示的是一个人工神经细胞。其中: 输入(Input);权重(Weight):左边五个灰色圆底字母w代表浮点数;激励函数(Activation Function):大圆,所有经过权重调整后的输入加起来,形成单个的激励值;输出(Output):神经细胞的输出。 进入人工神经细胞的每一个input(输入)都与一个权重w相联系,正是这些权重将决定神经网络的整体活跃性。假设权重为-1和1之间的一个随机数,权重可正可负(激发和抑制作用)。当输入信号进入神经细胞时,它们的值将与它们对应的权重相乘,作为图中大圆的输入。如果激励值超过某个阀值(假设阀值为1.0),就会产生一个值为1的信号输出;如果激励值小于阀值1.0,则输出一个0。这是人工神经细胞激励函数的一种最简单的类型。涉及的数学知识如下图所示: 如果最后计算的结果激励值大于阈值1.0,则神经细胞就输出1;如果激励值小于阈值则输出0。这和一个生物神经细胞的兴奋状态或抑制状态是等价的。下面图是通过神经网络实现逻辑表达式与运算:(参考NG斯坦福机器学习讲义) 可以看到x1和x2变量作为神经网络的输入,当它们取不同的0或1值时,其结果通过sigmod函数计算的值是不同的。它模拟了整个AND运算。 该图中神经网络共有三层 ( 注输入层不是神经细胞,神经细胞只有两层 ): 输入层中的每个输入都馈送到了隐藏层,作为该层每一个神经细胞的输入;然后,从隐藏层的每个神经细胞的输出都连到了它下一层(即输出层)的每一个神经细胞。 注意: 1.图中仅仅画了一个隐藏层,作为前馈网络,一般地可以有任意多个隐藏层。但在对付你将处理的大多数问题时一层通常是足够的。 2.事实上,有一些问题甚至根本不需要任何隐藏单元,你只要把那些输入直接连结到输出神经细胞就行了。 3.每一层实际都可以有任何数目的神经细胞,这完全取决于要解决的问题的复杂性。但神经细胞数目愈多,网络的工作速度也就愈低,网络的规模总是要求保持尽可能的小。 神经网络体系创建成功后,它必须接受训练来认出数字4,方法: 1.先把神经网络的所有权重初始化为任意值; 2.然后给他一系列输入代表面板不同配置的输入,对每种输入配置,检查它的输出是什么,并调整相应的权重; 3.如果我们送给网络的输入模式不是4,则我们知道网络应该输出一个0。因此每个非4字符时,网络权重应进行调整,使得它的输出趋向于0;当代表4的模式输送给网络时,则应把权重调整到使其输出趋向于1; 4.我们可以进一步识别0到9的所有数字或字母,其本质是手写识别的工作原理。 5.最后,网络不单能认识已经训练的笔迹,还显示了它有显著的归纳和推广能力。 正是这种归纳推广能力,使得神经网络已经成为能够用于无数应用的一种无价的工具,从人脸识别、医学诊断,直到跑马赛的预测,另外还有电脑游戏中的bot(作为游戏角色的机器人)的导航,或者硬件的robot(真正的机器人)的导航。 上图会演示神经网络在图像识别领域的一个著名应用,这个程序叫做LeNet,是一个基于多个隐层构建的神经网络。通过LeNet可以识别多种手写数字,并且达到很高的识别精度与拥有较好的鲁棒性。LeNet的发明人是机器学习的大牛Yann LeCun(目前google)。 右下方的方形中显示的是输入计算机的图像,方形上方的红色字样“answer”后面显示的是计算机的输出。左边的三条竖直的图像列显示的是神经网络中三个隐藏层的输出,可以看出,随着层次的不断深入,越深的层次处理的细节越低,例如层3基本处理的都已经是线的细节了。 推荐我自己非常喜欢的&牛逼的CSDN机器学习"一只鸟的天空"博主的一篇文章: 当今世界最NB的25位大数据科学家 这种类型的训练称作有监督的学习(supervised learnig),用来训练的数据称为训练集(training set)。调整权重可以采用许多不同的方法。对本类问题最常用的方法就是反向传播(backpropagation,简称backprop或BP)方法,即BP神经网络。 你自己可以去学习另外的一种训练方式,即根本不需要任何导师来监督的训练,或称无监督学习(unsupervised learnig)。 下图是神经网络的简单回顾与总结: 最后给大家看一个利用神经网络对图片进行分类的例子:过程就不详细论述了,图片很清晰,对人、汽车、摩托车、卡车进行图片识别,而具体的隐藏层函数需要大家去深入研究,我自己研究得也很浅显,抱歉~ 参考资料包括NG教授的课程讲义和CSDN一位很厉害的女博主Rachel-Zhang: Stanford机器学习---第五讲. 神经网络的学习 Neural Networks learning 三. 监督学习和无监督学习 因为给刚入学的学弟们讲的PPT,所以也简单讲述了监督学习和无监督学习的区别。下图是刘斌《Web数据挖掘》书的整体框架,当年我讲过。 在给它们解释监督学习和无监督学习的过程中,主要通过知乎的五个问题进行讲解。 第一个问题:什么是学习(Learning)? 一个成语就可以概括:举一反三 。以高考为例,高考的题目在上考场前我们未必做过,但在高三做过很多题目,懂得解题方法,因此考场上也能算出答案。 机器学习的思路类似:我们能不能利用一些训练数据(已经做过的题)使机器能够利用它们(解题方法)分析未知数据(高考题目)。 第二个问题:最普遍也是最简单的机器学习算法? 分类(Classification):输入的训练数据有特征(feature),有标签(label)。学习的本质就是找到特征和标签间的关系(mapping)。这样当有特征而无标签的未知数据输入时,我们可以通过已有的关系得到未知数据的标签。 第三个问题:上述分类所有训练数据都有标签,如果没有呢? 所有训练数据都有标签则为有监督学习(Supervised Learning),如果数据没有标签则是无监督学习(Unsupervised Learning),也即聚类(Clustering)。但有监督学习并非全是分类还有回归(Regression)。 无监督学习本身的特点使其难以得到如分类一样近乎完美的结果。这也正如我们在高中做题,答案(标签)是非常重要的,假设两个完全相同的人进入高中,一个正常学习,另一人做的所有题目都没有答案,那么想必第一个人高考会发挥更好,第二个人会发疯。 第四个问题:既然分类如此之好,聚类如此之不靠谱,那为何我们还可以容忍聚类的存在? 在实际应用中,标签的获取常常需要极大的人工工作量,有时甚至非常困难。例如在自然语言处理NLP中,Penn Chinese Treebank在2年里只完成了4000句话的标签。 第五个问题:难道有监督学习和无监督学习就是非黑即白的关系吗?有没有灰呢? Good idea。灰是存在的。二者的中间带就是半监督学习(semi-supervised learning)。对于半监督学习,其训练数据的一部分是有标签的,另一部分没有标签,而没标签数据的数量常常极大于有标签数据数量(这也是符合现实情况的)。 四. 总结 这篇文章主要是一篇基础介绍神经网络和机器学习的入门文章,同时参考了很多资料,主要是自己分享的PPT给学弟的新知识。同时由于我自己也只是入门,所以很多高深的东西也在学习,在此需要向上面文章中的个位大牛虚心学习。但作为入门文章,感觉还是不错的~ 最近发生的事情太多太多,其中包括拒绝了一些互联网公司或航天院的offer、面试,一心准备回家乡贵州的一些大学教书。怎么说呢?贵州那里确实承载了太多的东西,有亲情、有梦想,更有一些根深蒂固的东西。从小就在校园长大,父母都是老师,家住校园,到了高中也独自去到了省会贵阳住校三年,大学更是来到了北理,异地他乡的我,一呆就是六年。从小玩到大的姐姐也成为了初中老师,似乎我这一生与学校挂钩后,就再也脱不了关系了。 这并没有什么情怀,没有什么了不起,更没有什么高大上。更多的是自己从小一直希望成为一名教师吧!尽管父母都不愿意让孩子再从事这一行业,但我已毅然决定。确实,我自己也舍弃了很东西,如每月的高工资福利、发布一款产品后的欣喜若狂等等,但同时有舍才有得,希望自己能在大学中获得一些东西吧!还是《当幸福来敲门》那句话: 在你的人生路上,总有很多人说你这也不行,那也不行,梦想是你自己的;有梦想就需要学会自己去保护它。 别拿着青春的幌子,浪费自己的年轻时光。我一直都喜欢深夜写文章,深夜是思考的好时间,更是寂寞的好时光。一个人行走在这个世界,很多路都需要自己去孤独的前行,需要让自己的内心强大起来。但每每写完一篇博客或想到当老师上课,都能让我心灵为之一颤。这就够了~ 下面是我看到吴祖增老师80多岁高龄后,依然坚持写作的感慨!见笑了~ 侠之为大,为国为民。行止无定,随遇而安。心安乐处,便是深安乐处。 最后希望自己能够找到一所大学,成为一名教师!同时也希望文章对你有所帮助~ (By:Eastmount 2015-11-03 深夜5点 http://blog.csdn.net/eastmount/)
前言: 作为一名从小就看篮球的球迷,会经常逛虎扑篮球及湿乎乎等论坛,在论坛里面会存在很多精美图片,包括NBA球队、CBA明星、花边新闻、球鞋美女等等,如果一张张右键另存为的话真是手都点疼了。作为程序员还是写个程序来进行吧! 所以我通过Python+Selenium+正则表达式+urllib2进行海量图片爬取。 前面讲过太多Python爬虫相关的文章了,如爬取新浪博客、维基百科Infobox、百度百科、游迅网图片,也包括Selenium安装过程等等,详见我的两个专栏: Python学习系列 Python爬虫之Selenium+Phantomjs+CasperJS运行效果: 运行效果如下图所示,其中第一幅图是虎扑网站爬取tag(标签)为马刺的图集,第二幅图是爬取tag为陈露的图集。每个文件夹命名对应网页主题,而且图片都是完整的。 http://photo.hupu.com/nba/tag/马刺 http://photo.hupu.com/nba/tag/陈露 源代码: # -*- coding: utf-8 -*- """ Crawling pictures by selenium and urllib url: 虎扑 马刺 http://photo.hupu.com/nba/tag/%E9%A9%AC%E5%88%BA url: 虎扑 陈露 http://photo.hupu.com/nba/tag/%E9%99%88%E9%9C%B2 Created on 2015-10-24 @author: Eastmount CSDN """ import time import re import os import sys import urllib import shutil import datetime from selenium import webdriver from selenium.webdriver.common.keys import Keys import selenium.webdriver.support.ui as ui from selenium.webdriver.common.action_chains import ActionChains #Open PhantomJS driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe") #driver = webdriver.Firefox() wait = ui.WebDriverWait(driver,10) #Download one Picture By urllib def loadPicture(pic_url, pic_path): pic_name = os.path.basename(pic_url) #删除路径获取图片名字 pic_name = pic_name.replace('*','') #去除'*' 防止错误 invalid mode ('wb') or filename urllib.urlretrieve(pic_url, pic_path + pic_name) #爬取具体的图片及下一张 def getScript(elem_url, path, nums): try: #由于链接 http://photo.hupu.com/nba/p29556-1.html #只需拼接 http://..../p29556-数字.html 省略了自动点击"下一张"操作 count = 1 t = elem_url.find(r'.html') while (count <= nums): html_url = elem_url[:t] + '-' + str(count) + '.html' #print html_url ''' driver_pic.get(html_url) elem = driver_pic.find_element_by_xpath("//div[@class='pic_bg']/div/img") url = elem.get_attribute("src") ''' #采用正则表达式获取第3个<div></div> 再获取图片URL进行下载 content = urllib.urlopen(html_url).read() start = content.find(r'<div class="flTab">') end = content.find(r'<div class="comMark" style>') content = content[start:end] div_pat = r'<div.*?>(.*?)<\/div>' div_m = re.findall(div_pat, content, re.S|re.M) #print div_m[2] link_list = re.findall(r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')", div_m[2]) #print link_list url = link_list[0] #仅仅一条url链接 loadPicture(url, path) count = count + 1 except Exception,e: print 'Error:',e finally: print 'Download ' + str(count) + ' pictures\n' #爬取主页图片集的URL和主题 def getTitle(url): try: #爬取URL和标题 count = 0 print 'Function getTitle(key,url)' driver.get(url) wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='piclist3']")) print 'Title: ' + driver.title + '\n' #缩略图片url(此处无用) 图片数量 标题(文件名) 注意顺序 elem_url = driver.find_elements_by_xpath("//a[@class='ku']/img") elem_num = driver.find_elements_by_xpath("//div[@class='piclist3']/table/tbody/tr/td/dl/dd[1]") elem_title = driver.find_elements_by_xpath("//div[@class='piclist3']/table/tbody/tr/td/dl/dt/a") for url in elem_url: pic_url = url.get_attribute("src") html_url = elem_title[count].get_attribute("href") print elem_title[count].text print html_url print pic_url print elem_num[count].text #创建图片文件夹 path = "E:\\Picture_HP\\" + elem_title[count].text + "\\" m = re.findall(r'(\w*[0-9]+)\w*', elem_num[count].text) #爬虫图片张数 nums = int(m[0]) count = count + 1 if os.path.isfile(path): #Delete file os.remove(path) elif os.path.isdir(path): #Delete dir shutil.rmtree(path, True) os.makedirs(path) #create the file directory getScript(html_url, path, nums) #visit pages except Exception,e: print 'Error:',e finally: print 'Find ' + str(count) + ' pages with key\n' #Enter Function def main(): #Create Folder basePathDirectory = "E:\\Picture_HP" if not os.path.exists(basePathDirectory): os.makedirs(basePathDirectory) #Input the Key for search str=>unicode=>utf-8 key = raw_input("Please input a key: ").decode(sys.stdin.encoding) print 'The key is : ' + key #Set URL List Sum:1-2 Pages print 'Ready to start the Download!!!\n\n' starttime = datetime.datetime.now() num=1 while num<=1: #url = 'http://photo.hupu.com/nba/tag/%E9%99%88%E9%9C%B2?p=2&o=1' url = 'http://photo.hupu.com/nba/tag/%E9%A9%AC%E5%88%BA' print '第'+str(num)+'页','url:'+url #Determine whether the title contains key getTitle(url) time.sleep(2) num = num + 1 else: print 'Download Over!!!' #get the runtime endtime = datetime.datetime.now() print 'The Running time : ',(endtime - starttime).seconds main() 代码解析: 源程序主要步骤如下: 1.入口main函数中,在E盘下创建图片文件夹Picture_HP,然后输入图集url,本打算输入tag来进行访问的,因为URL如下: http://photo.hupu.com/nba/tag/马刺 但是解析URL中文总是错误,故改成输入URL,这不影响大局。同时你可能发现了代码中while循环条件为num<=1,它只执行一次,建议需要下载哪页图集,就赋值URL即可。但是虎扑的不同页链接如下,通过分析URL拼接也是可以实现循环获取所有页的。 http://photo.hupu.com/nba/tag/%E9%99%88%E9%9C%B2?p=2&o=1 2.调用getTitle(rul)函数,通过Selenium和Phantomjs分析HTML的DOM结构,通过find_elements_by_xpath函数获取原图路径URL、图集的主题和图片数量。如图: 通过该函数即可获取每个图集的主题、URL及图片个数,同时根据图集主题创建相应的文件夹,代码中涉及正则表达式获取图片数量,从"共19张"到数字"19"。如图: 3.再调用函数getScript(elem_url, path, nums),参数分别是图片url、保存路径和图片数量。那么如何获取下一张图片的URL呢? 当通过步骤二爬取了图集URL,如:http://photo.hupu.com/nba/p29556.html (1).如果是通过Ajax、JavaScript动态加载的图片,url无规律则需要调用Selenium动态模拟鼠标操作点击“下一张”来获取原图url; (2).但很多网站都会存在一些规律,如虎扑的第九张图片链接如下,通过URL字符串分割处理即可实现:"p29556-"+"数字"+".html"即可。 http://photo.hupu.com/nba/p29556-9.html 在该函数中,我第一次也是通过Selenium分析HTML结构获取原始图片url,但每张图片都需要调用一次Phantomjs无界面浏览器,这速度太慢了。故该成了正则表达式获取HTML中的原图URL,其原因如下图: 虎扑又偷懒了,它在下面定义了原图链接,直接获取即可。 4.最后一步即urllib.urlretrieve(pic_url, pic_path + pic_name)下载图片即可。 当然你可能会遇到错误“Error: [Errno 22] invalid mode ('wb') or filename”,参考 stackoverflow 总结: 这是一篇讲述Selenium和Python爬取虎扑图集的文章,文章内容算是爬虫里面比较基础的,其中下载的“陈露”图片和网站给出的34个图集、902张图片一样。同时采用正则后时间估计3分钟左右,很快~当然,虎扑里面的标签很多,足球应该也是类似,只要修改URL即可下载图集,非常之方便。 最近在学习Spider更为广泛的Python爬取,也准备学习分布式爬虫、docker等。希望以后有机会真正讲讲如何实现深度搜索爬取和宽度搜索爬取等相关深层次内容,不要在这么水了。当然,如果你是爬虫初学者或Python初学者,这些实践的东西都将对你有所帮助~ 最后希望读到此处的朋友,能收获一些东西,如果有错误或不足之处,还请海涵~最近正在认真学习中,非常期望自己能成为一名大学老师,无知 · 乐观 · 谦逊 · 低调 · 生活。 (By:Eastmount 2015-10-25 深夜3点 http://blog.csdn.net/eastmount/)
写了这么多年的C代码,回过头来再看《The C Programming Language》这本书,作者Brian W. Kernighan和C语言之父Dennis M. Ritchie。感觉里面的知识和书的架构给人非常”合理”的感觉。怎么个合理法呢? 首先书中的代码,如else-if中使用binsearch函数介绍二分查找、atoi介绍字符串s转换为整数、计算器逆波兰表达式,都是实际中非常经典且常用的知识; 然后书中大部分的程序都是基于stdlib.h、string.h、ctype.h等这些源文件并且简化后的代码,可见作者对C语言的了解程度不是一般,这也是有别于国内的C语言书籍的地方; 最后书中的整体逻辑架构非常不错,而且书中的例子是串联起来的,如push和pop函数,而且通俗易懂并结合了Unix的相关知识,毕竟C语言和Unix之父。 总体感觉一句话”还君明珠双泪垂,恨不相逢未嫁时”,如果时光可以倒流,当时还是应该看些经典的书籍啊! 这篇文章主要是我的读书笔记,记录C语言中一些比较难或经典的知识,希望能勾起你的回忆。如果对你也有所帮助,那我就非常满足了;如果有错误或不足之处,还请海涵~ 第2章 类型、运算符与表达式 数据类型及长度 int通常代表特定机器中整数的自然长度,short类型通常为16位,long类型通常为32位,int类型可以为16位或32位。各编译器可以根据硬件特性自主选择合适的类型长度,但要遵循下列限制:short与int类型至少为16位,而long类型至少为32位,并且short类型不得长于int类型,而int类型不得长于long类型。 类型限定符signed与unsigned用于限定char类型或任何整型。unsigned类型数总是正值或0,并遵循算术模 2n 定律,其中n是该类型占用的位数。例如char对象占用8位,那么unsigned char类型变量的取值范围为0~255,而signed char类型变量的取值范围为-128~127(在采用对二的补码的机器上)。 long double类型表示高精度的浮点型。同整型一样,浮点型的长度也取决于具体的实现,float、double与long double类型可以表示相同的长度,也可以表示两种或三种不同的长度。有关这些类型长度定义的符号常量及其它与机器和编译器有关的属性可以在标准头文件< limits.h >与< float.h >中找到,这些内容在附录B中。 格式说明可以忽略宽度与精度,其中%6f表示待打印的浮点数至少有6个字符宽;%.2f指定待打印的浮点数的小数点后有两位小数,但宽度没有限制;%f则仅仅要求按照浮点数打印该数。同时,printf函数还支持下列格式说明:%o表示八进制数;%x表示十六进制数;%c表示字符;%s表示字符串;%%表示百分号(%)本身。 常量 strlen ANSI C语言中的全部转移字符序列如下所示: 其中’\ooo’表示任意的字节大小的为模式,代表1~3个八进制数字(0…7);’\xhh’其中hh是一个或多个十六进制数字(0…9,a…f,A…F)。例如: #define VTAB '\013' /* ASCII vertical tab */ #define VTAB '\xb' /* ASCII vertical tab */ 字符常量’\0’表示值为0的字符,也就是空字符(null)。 常量表达式是仅仅只包含常量的表达式,这种表达式在编译时求值,而不在运行时求值,它可以出现在常量可以出现的任何位置。如下常用语定义数组长度大小: #define MAXLINE 1000 char line[MAXLINE+1] 字符串常量是用双括号括起来的0个或多个字符组成的字符序列,如”I am a string”。其中空字符串用”“表示,字符串中使用 \” 表示双引号字符。其实,字符串常量就是字符数组,使用空字符’\0’作为串的结尾。因此,存储字符串的物理存储单元数比括在双引号中的字符数多一个。 这种表示方法也说明,C语言对字符串的长度没有限制,但程序必须扫描完整个字符串后才能确定字符串的长度。标准库函数strlen(s)可以返回字符串参数s的长度,但长度不包括末尾’\0’。代码如下: /* strlen: return length of s */ int strlen(char s[]) { int i; while(s[i] != '\0') ++i; return i; } 注意:我们需弄清字符常量和仅包含一个字符的字符串之间的区别:’x’与”x”是不同的。前者是一个整数,其值是字母x在机器字符集中对应的数值;后者是包含一个字符(即字母x)以及一个结束符’\0’的字符数组。 枚举常量 enum 枚举常量是另外一种类型的常量。枚举是一个常量整型值的列表,如: enum boolean { NO, YES}; 在没有显示说明的情况下,enum类型中第一个枚举名的值为0,第二个为1,依次类推。如果只指定了部分枚举名的值,那么未指定值的枚举名的值将依着最后一个指定值向后递增。 例:其中FEB的值为2,MAR的值为3,依次类推。不同枚举中的名字必须互不相同,同一枚举中不同的名字可以具有相同的值。 enum months { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } 枚举为建立常量值与名字之间的关联提供了一种便利的方式。相对于#define语句来说,它的优势在于常量值可以自动生成。尽管可以声明enum类型的变量,但编译器不检查这种类型的变量中存储的值是否为该枚举的有效值。不过,枚举变量提供这种检查,因此枚举比#define更具优势。 声明 const 所有变量都必须先声明后使用,尽管某些变量可以通常上下文隐式地声明。一个声明指定一种变量类型,后面所带的变量表可以包含一个或多个该类型的变量。 int lower, upper, step; char c, line[1000]; 还可以在声明的同时对变量进行初始化。常见的如下: int i = 0; int limit = MAXLINE + 1; float eps = 1.0e-5; 默认情况下,外部变量与静态变量将被初始化为0。未经显示初始化的自动变量的值为未定义值(即无效值)。 任何变量的声明都可以使用const限定符限定。该限定符指定变量的值不能被修改,对数组而言,const限定符指定数组所有元素的值不能被修改。 const double e = 2.71828282845905; const char msg[] = "warning :"; const限定符也可配合数组参数使用,它表明函数不能修改数组元素的值。如果试图修改const限定符限定的值,其结果取决于具体的实现。 int strlen(const char[]); 类型转换 atoi 当一个运算符的几个操作数类型不同时,就需要通过一些规则把它们转换为某种共同的类型。一般来说,自动转换是指把“比较窄的”操作数转换为“比较宽的”操作数,并且不丢失信息的转换。例如在计算表达式f+i时,将整形变量i的值自动转换为浮点型(f此处为浮点型)。 由于char类型就是较小的整形,因此在算术表达式中可以自由使用char类型的变量,这就为实现某些字符转换提供了很大的灵活,如下atoi函数将一串数字转换为相应的数值。 /* atoi: convert s to integer */ int atoi(char s[]) { int i, n; n = 0; for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i) n = 10 * n + (s[i] - '0'); return n; } 当然,在任何表达式中都可以使用一个称为强制转换的一元运算符强制进行显示类型转换。 (类型名) 表达式 例如库函数sqrt的参数为double类型,如果处理不当,结果可能会无意义(sqrt在< math.h >中声明)。因此,如果n是整数可以使用sqrt((double) n),在把n传递给函数前将其转换为double类型。 注意:在通常情况下,参数是通过函数原型声明的。这样,当函数被调用时,声明将对参数进行自动强制转换。例如对于sqrt的函数原型:double sqrt(double) 当调用 root =sqrt(2) 时,不需要使用强制类型转换运算符就可以自动将整数2强制转换为double类型的2.0。 自增和自减运算符 strcat C语言中提供了两个用于变量递增与递减的特俗运算符。自增运算符++使其操作数递增1,自减运算符–使其操作数递减1。 (1)++n:先将n的值递增1,然后再使用变量n的值 (2)n++:先使用变量n的值,然后再将n的值递增1 n = 5; x = n++; //x的值置于5 n=6 m = 5; y = ++m; //y的值置于6 m=6 下面举两个例子。函数squeeze(s, c)表示删除字符串s中出现的所有字符c: /* squeeze: delete all c from s */ void squeeze(char s[], int c) { int i, j; for(i = j = 0; s[i] != '\0'; i++) if(s[i] != c) s[j++] = s[i]; s[j] = '\0'; } 再举个例子标准函数strcat(s, t),它将字符串t连接到字符串s的尾部。函数strcat假定字符串s中有足够的空间保存这两个字符串连接的结果。(标准库中的该函数会返回一个指向新字符串的指针)。 void strcat(char s[], char t[]) { int i, j; i = j = 0; while (s[i] != '\0') /* find end of s */ i++; while ((s[i++] = t[j++]) != '\0') /* copy t */ ; } 在将t中的字符逐个拷贝到s的尾部时,变量i和j都使用后缀运算符++,从而保证循环过程中i和j均指向下一个位置。 按位运算符 C语言提供了6个位操作运算符。这些运算符只能作用于整形操作数,即只能作用于带符号或无符号char、short、int、long类型。 & 按位与(AND) | 按位或(OR) ^ 按位异或(XOR) << 左移 >> 右移 ~ 按位求反(一元运算符) (1) 按位与运算&经常用于屏蔽某些二进制位。 如 n = n & 0177 ,该语句将n中除7个低二进制位外的其他各位均置为0。 (2) 按位或运算|常用于将某些二进制位置为1。 如 x = x | SET_ON 该语句将x中对应于SET_ON中为1的那些二进制位置为1。 (3) 按位异或^表示当两个操作数的对应位不相同时,将该位设置为1,否则为0。 (4) 表达式x<<2表示将x的值左移2位,右边空出的2位用0填补,该表达式等价于左操作数乘以4。而>>表示右移,在对unsigned类型的无符号值进行右移位时,左边空出的部分用0填补,当对signed类型的带符号值进行右移时,某些机器将对左边空出的部分用符号位填补(即算术移位),而另一些机器则对左边空出部分用0进行填补(即逻辑移位)。 例如getbits(x, p, n)函数返回x中从右边数第p位开始向右数n位的字段,假定最右边的一位是第0为,n与p都是正值。getbits(x, 4, 3)返回x中第4、3、2三位的值。 /* getbits: get n bits from position p */ unsigned getbits(unsigned x, int p, int n) { return (x >> (p+1-n)) & ~(~0 << n); } 其中,表达式m >> (p+1-n)将期望获得的字段移位到字的最右端,即7 6 5 4 3 2 1 0 右移(4+1-3)=2个字段,得到0 0 7 6 5 4 3 2。然后~0的所有位都为1,语句 ~0 << n 将全1向左移动n位,即1 1 1 1 1 0 0 0, 再取反将最右边的n位置为1的屏蔽,即0 0 0 0 0 1 1 1 。 赋值运算与条件语句 赋值运算符有时还有助于编译器产生高效代码,下面是最常见的一个: while((c = getchar()) != EOF) EOF:end of file,文件结束。定义在头文件< stdio.h >中,是个整型数,其具体数值是什么并不重要,只要它与任何char类型的值都不相同即可。这里使用符号常量,可以确保程序不需要依赖于其对应的任何特定的数值。 上面面种输入集中化代码,缩短了程序,使整个程序看起来更紧凑,这种风格也会让读者更易阅读。不过,如果过多的使用这种类型的复杂语句,编写程序会很难理解,应尽量避免这种情况。 同时,由于不等式运算符!=的优先级比赋值运算符=的优先级高,故赋值表达式两边的圆括号不能省略。 下面代码是通过函数bitcount统计其整形参数的值为1的二进制位的个数: /* bitcount: count 1 bits in x */ int bitcount(unsigned x) { int b; for (b = 0; x != 0; x >>= 1) if (x & 01) b++; return b; } 终于知道LeetCode那道“Number of 1 Bits”题目的出处了,还可以通过x & (x-1)删除最右边为1的二进制。 /* bitcount: count 1 bits in x */ int bitcount(unsigned x) { int b; while(x != 0) { x &= (x - 1); b++; } return b; } 条件语句可以使用三元运算符(“? :”),表达式如下: expr1 ? expr2 : expr3 它首先计算expr1,如果其值不等于1(为真)则计算expr2的值,并以该值作为条件表达式的值,否则计算expr3的值,并以该值为条件表达式的值。expr2和expr3只能有一个表达式被计算,例如: z = (a > b) ? a: b; /* z = max(a,b) */ 第3章 控制流 else-if语句 binsearch 在介绍else-if语句中,书中通过折半查找进行讲解。输入值x与数组v的中间元素进行比较: (1)如果x小于中间元素的值,则在该数组的前半部分查找 (2)否则在数组的后半部分查找 (3)再将x与所选部分的中间元素进行比较,直到找到或查找范围为空 代码如下: /* binsearch: find x in v[0]<=v[1]<=...<=v[n-1] */ int binsearch(int x, int v[], int n) { int low, high, mid; low = 0; high = n-1; while(low <= high) { mid = (low+high)/2; if (x < v[mid]) high = mid + 1; else if (x > v[mid]) low = mid + 1; else /* found match */ return mid; } return -1; /* no match */ } switch语句 switch语句是一种多路判定语句,如果某个分支与表达式的值匹配,则从该分支开始执行。如果没有哪一个分支能匹配表达式,则执行default分支(可选)。 注意:在switch局域中,case的作用知识一个标号,因此某个分支中代码执行完后,程序进入下一分支继续执行。除非程序中显示跳转,通过break或return语句。建议补全break和default。 统计数字、空格代码如下: switch (表达式) { case 常量表达式: 语句序列 case 常量表达式: 语句序列 default: 语句序列 } while((c = getchar()) != EOF) { switch (c) { case '0': case '1': ... case '9': digit[c-'0']++; break; case ' ': case '\n': case '\t': white++; break; default: other++; break; } } do-while语句 itoa 在程序设计时使用while循环语句还是for循环语句,主要取决于程序设计人员的个人偏爱。 如果没有初始化等操作,使用while循环更自然一些,如while( (c=getchar()) != EOF )。 如果语句中需要执行简单初始化和变量递增,使用for语句更适合,它将循环控制语句集中放在循环的开头,结构更紧凑、更清晰,如 for(i=0; i < n; i++)。 do-while循环是在循环体执行后测试终止条件,这样循环体至少被执行一次。 do-while循环语句在某些情况下还是很有用的,如下通过函数itoa将数字转换成字符串,数字0也需转换一次。 /* itoa: convert n to characters in s */ void itoa(int n, char s[]) { int i, sign; if ((sign = n) < 0) /* record sign */ n = -n; /* make n positive */ i = 0; do { /* generate digits in reverse order */ s[i++] = n % 10 + '0'; } while( (n /= 10) > 0); if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); /* 字符串翻转 */ } goto语句 C语言提供了可随意滥用的goto语句及标记跳转位置的标号。所有使用了goto语句的程序都能改成不带goto语句的程序,而且大多数情况下,使用goto语句的程序段比不使用goto语句的程序段要难以理解和维护,除少数情况。 建议尽量可能少地使用goto语句。某些场合下goto语句可以终止程序在某些深度嵌套的结构中的处理过程,如下判断a与b数组是否具有相同元素: for (i = 0; i < n; i++) for (j = 0; j < m; j++) if(a[i] == b[j]) goto found; /* did't find any common element */ ... found: /* got one: a[i] == b[j] */ ... 第4章 函数与程序结构 函数基础知识 atof 函数的定义形式如下: 返回值类型 函数名 (函数声明表) { 声明和语句 } 函数定义中的各构成部分都可以省略,最简单的函数如下所示: dummy() { } 该函数不执行任何操作也不返回任何值。这种不执行任何操作的函数有时很有用,它可以在程序开发期间用以保留位置(留待以后填充代码)。如果函数定义中省略了返回值类型,则默认为int类型。 被调用函数通过return语句向调用者返回值,也可以返回任何表达式。 前面讨论的函数都是不返回任何值(void)或只返回int类型值的函数,现在介绍一个不是高质量的类似于标准库中包含 atof 函数,它将字符串s转换为相应的双精度浮点数,在头文件”stdlib.h”中。 #include <ctype.h> /* atof: convert string s to double */ double atof(char s[]) { double val, power; int i, sign; for (i=0; isspace(s[i]); i++) /* skip white space */ ; sign = (s[i] == '-') ? -1 : 1; if (s[i] == '+' || s[i] == '-') i++; for (val = 0.0; isdigit(s[i]); i++) val = 10.0 * val + (s[i] - '0'); if (s[i] == '.') i++; for (power = 1.0; isdigit(s[i]); i++) { val = 10.0 * val + (s[i] - '0'); power *= 10; } return sign * val / power; } 代码的核心是通过计算val数字除以小数点后面的power(10的倍数,如12.345为12345除以1000)。同时引用了 ctype.h 中的 isspace (检查ch是否是空格符和跳格符或换行符)和 isdigit (检查ch是否是数字0-9)。 外部变量 逆波兰表示法 1.外部变量定义在函数之外,可以在许多函数中使用。 由于C语言不允许在一个函数中定义其它函数,因此函数本身是”外部的”。由于外部变量可以在全局范围内访问,这就可以替代通过函数参数与返回值这种数据交换方式。 如果函数之间需要共享大量的变量,使用外部变量比使用一个很长的参数表更方便、有效。但是这样做必须谨慎,因为这可能会对程序结构产生不良的影响,而且可能会导致程序中各个函数之间具有太多的数据联系。 2.外部变量的用途还表现在它们与内部变量相比具有更大的作用域和更长的生存期。 自动变量只能在函数内部使用,从函数被调用时存在到函数退出时变量消失。而外部变量是永久存在的,它们的值在一次函数调用到下一次函数调用之间保持不变。因此如果两个函数必须共享某些数据,而这两个函数互不调用对方,这种情况最方便的方式就是共享数据定义为外部变量,而不是作为函数参数传递。 书中例子:编写一个具有加减乘除四则运算功能的计算器程序 采用逆波兰表示法替代普通的中缀表示法,如下列中缀表达式:(1 - 2) * (4 + 5) 采用逆波兰表示法为:1 2 - 4 5 + * 基本思路: 每个操作数都被依次压入栈中:当一个运算符到达时,从栈中弹出相应数目的操作数(对二元运算来说是两个操作数),把该运算符作用于弹出的操作数,并把运算结果再压入栈中。 如上1和2入栈,当’-‘时作减法,值-1取代它们;然后将4和5压入栈中,再’+’作加法得9取代它们;最后从栈中取出栈顶的-1和9,并把它们的乘积-9压入到栈顶,到达输入行末尾时,把栈顶的值弹出并打印。 此时需要在main函数外定义外部变量“int sp = 0;”指向栈顶和数组“double val[MAXVAL];”,再对它们进行push(进栈)和pop(出栈)函数操作。 作用域规则 名字的作用域指的是程序中可以使用该名字的部分。 对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。不同函数中声明的具有相同名字的各个局部变量之间没有任何关系。函数的参数也是这样,可看作是局部变量。 外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。 如下列代码中:push和pop可以访问变量sp,而main函数不行,因为它定义在main函数之后;同样push和pop函数也不能在main函数中使用。 main() {...} int sp = 0; double val[MAXVAL]; void push(double f) {...} void pop(void) {...} 注意:如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一源文件中,则必须在相应的变量声明中强制性地使用关键字extern. 外部变量定义与外部变量声明 extern 将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量定义除此之外还将引起存储器的分配。 1.外部变量定义 如果将下列语句放在所有函数的外部:那么这两条语句将定义外部变量sp与val,并为之分配存储单元,同时这两条语句还可以作为该源文件中其余部分的声明。 int sp; double val[MAXVAL]; 2.外部变量声明 而下面的两行语句:为源文件的其余部分声明一个int类型的外部变量sp及一个double数组类型的外部变量val(该数组的长度在其它地方确定),但这两个声明并没有建立变量或为它们分配存储单元。 extern int sp; extern double val[]; 在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问它。外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度,如上面的变量val[]。同时,外部变量的初始化只能出现在其定义中。 总之,extern可置于变量或者函数前,以表示变量或者函数的定义在别的文件中或声明之后,提示编译器遇到此变量或函数时,在其它模块中或之后寻找其定义。 举个例子:函数push与pop定义在一个文件file1中,而变量val与sp在file2件中定义并初始化,下面代码将其定义与声明”绑定“在一起。同样,如果要在同一个文件中先使用、后定义sp与val,也需要按照这种方式来组织文件。 file1 文件: extern int sp; extern double val[]; void push(double f) {...} double pop(void) {...} file2 文件: int sp = 0; double val[MAXVAL]; 静态变量 static 某些变量如文件stack.c中定义的变量sp与val,它们仅供其所在的源文件中的函数使用,其他函数不能访问。用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。 通过static限定外部对象,可以达到隐藏外部对象的目的,比如getch-ungetch结构需要共享buf与bufp两个变量,这样buf与bufp必须是外部变量,但这两个对象不应该被getch与ungetch函数的调用者所访问。 要将对象指定为静态存储,可以在正常的对象声明之前加上关键字static作为前缀。如下所示放在一个文件中编译: static char buf[BUFSIZE]; /* buffer for ungetch */ static int bufp = 0; /* next free position in buf */ int getch(void) {...} void ungetch(int c) {...} 那么其他函数就不能访问变量buf与bufp,因此两个名字就不会和同一程序中的其他文件中的相同的名字相冲突。换句话说,外部静态变量和外部变量都是一种公用的全局变量,但外部静态变量的作用域仅仅是在定义它的那个文件中,出了该文件不管是否用extern说明都是不可见的。即: 外部静态变量仅仅作用于定义它的那个文件,而外部变量作用于整个程序。 外部的static声明通常多用于变量,当然,它也可以用于声明函数。通常情况下,函数名字是全局可访问的,对整个程序的各个部分而言都可见。但是如果把函数声明为static类型,则该函数名除了对该函数声明所在的文件可见外,其它文件都无法访问。 static也可用于声明内部变量。static类型的内部变量同自动变量一样,是某个特定函数的局部变量,只能在该函数中使用,但它与自动变量不同的是: 不管其所在函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。static会一直占据着存储空间,重复使用值会保留(静态变量存放在内存中的静态存储区)。 例如:静态变量只在第一次进入程序块时被初始化一次。 void inc() { static int x = 0; x++; printf("x = %d\n",x); } void main() { inc(); //输出 x = 1 inc(); //输出 x = 2 inc(); //输出 x = 3 return 0; } 寄存器变量 register register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是将register变量放在机器的寄存器中,使程序更小、执行速度更快。但编译器可以忽略此选项。声明如:register int x; register声明只适用于自动变量以及函数的形式参数,如下: f(register unsigned m, register long n) { register int i; ... } 实际使用时,底层硬件环境的实际情况对寄存器变量的使用会有一些限制,每个函数中只有很少的变量可以保存在寄存器中,且只允许某些类型的变量。但是,过量的寄存器声明并没有什么害处,这是因为编译器可以忽略过量的或不支持的寄存器变量声明。另外,无论寄存器变量实际上是不是存放在寄存器中,它的地址都是不能访问的。 递归 qsort C语言中的函数可以递归调用,即函数可以直接或间接调用自身。其中举个字符串递归翻转的例子: // 递归实现字符串反转 char *reverse(char *str) { int len; char ctemp; if( !str ) { return NULL; } len = strlen(str); if( len > 1 ) { ctemp =str[0]; str[0] = str[len-1]; str[len-1] = '/0'; //最后一个字符在下次递归时不再处理 reverse(str+1); //递归调用 str[len-1] = ctemp; } return str; } 其过程如下,假设现有字符串:a b c d e f 先调换 a 和 f 的位置,然后递归 | b c d e |,这里巧用str+1表示从前移动至b,而str[len-1]=’\0’表示从后移动至e,一次移动两个位置。递归之后再赋值str[len-1]=ctemp,即f位置赋值为a。 另一个比较好说明递归的例子是快速排序。参考我的博客: 彻底搞懂qsort 对于一个给定的数组,从中选择一个元素,该元素为界将余元素划分为两个子集,一个子集中的所有元素都小于该元素,另一个子集中的所有元素都大于或等于该元素。对这两个子集递归执行这一过程,当某个子集中的元素小于2时,这个子集就不再需要再次排序,递归终止。 /* qsort: sort v[left]...v[right] into increasing order */ void qsort(int v[], int left, int right) { int i,last; void swap(int v[],int i,int j); if(left>=right) //若数组包含的元素数少于两个 则推出递归结束 return; swap(v,left,(left+right)/2); //取中间元素作为划分子集的参考数,首先存储参考数于v[0] last=left; for(i=left+1;i<=right;++i) //划分子集 { if(v[i]<v[left]) swap(v,++last,i); } swap(v,left,last); //恢复划分子集的元素 qsort(v,left,last-1); qsort(v,last+1,right); } 这里之所以将数组元素交换操作放在一个单独的函数swap中,是因为它在qsort函数中要使用3次。 void swap(int v[], int i, int j) { int temp; temp=v[i]; v[i]=v[j]; v[j]=temp; } 注意:递归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栈。递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写与理解。在描述树等递归定义的数据结构时使用递归尤其方便,同时面试过程通常会让你使用栈来模拟二叉树递归遍历的过程。 C预处理器 C语言通过预处理器提供了一些语言功能。从概念上讲,预处理器是编译过程中单独执行第一个步骤。两个常用的预处理器指令时: #include 指令:用于在编译期间把指定文件的内容包含进当前文件中 #define 指令:用任意字符序列替代一个标记 1.文件包含 文件包含指令(即#include指令)使得处理大量的#define指令以及声明更加方便。在源文件中,任何形如: #include “文件名” #include <文件名> 的行都将被替换为由文件名指定的文件的内容。如果文件名用引号引起来,则在源文件所在位置查找该文件;如果在该位置没有找到文件,或者如果文件是用尖括号<与>括起来的,则根据相应的规则查找该文件,这个规则同具体的实现有关。被包含的文件本身也可包含#include指令。 源文件的开始通常会有多个#include指令,它们用以包含常见的#define语句和extern声明,或从头文件中访问库函数的函数原型声明,比如< stdio.h >。 在大的程序中,#include指令是将所有声明绑定在一起的较好的方法。它保证所有的源文件都具有相同的定义与变量声明,这样可以避免出现一些不必要的错误。如果某个包含文件的内容发生了变化,那么所有依赖于该包含文件的源文件都必须重新编译。 2.宏替换 宏定义形式如下: #define 名字 替换文本 这是一种最简单的宏替换——后续所有出现名字记号的地方都将被替换为替换文本。#define指令中的名字与变量名的命名方式相同,替换文本可以是任意字符串。 宏定义#define通常占一行,若干行时需要在待续的行末尾加上一个反斜杠符\。宏定义定义的名字的作用域从其定义点开始,到被编译的源文件的末尾处结束。宏定义可以使用前面出现的宏定义,替换只对记号进行,对括号中的字符串不起作用。例如宏定义YES在printf(“YES”)或YESMAN中将不执行替换。 宏定义也可以带参数,这样可以对不同的宏调用使用不同的替换文本。如: #define max(A, B) ((A) > (B) ? (A) : (B)) 使用宏max看起来像是函数词用,但宏调用直接将替换文本插入到代码中。形式参数的每次出现都将被替换成对应的实际参数。如: x = max(p+q, r+s); 将替换为: x = ((p+q) > (r+s) ? (p+q) : (r+s)); 如果对各种类型的参数的处理是一致的,则可以将同一个宏定义应用于任何数据类型,而无需针对不同的数据类型需要定义不同的max函数。 仔细考虑下max展开式,就会发现它存在一些缺陷。其中作为参数的表达式需要重复计算两次,如果含有自增或自减会出现不正确的情况。 同时宏定义尤其需要注意圆括号以保证计算次序的正确性。如: #define square(x) x * x 当执行square(5+1)时会替换为”5 + 1 * 1 +5”,其结果为11而不是25。 但是,宏定义还是很有价值的。< stdio.h >头文件中有一个很实用的例子: getchar 与 putchar 函数在实际中常常定义为宏,这样可以避免处理字符时调用函数所需的运行时开销。 < ctype.h >头文件中定义的函数也常常是通过宏实现的。 可以通过#undef指令取消名字的宏定义,这样可以保证后续的调用是函数调用而不是宏调用。 #undef getchar int getchar(void) { … } 预处理器运算符##为宏扩展提供了一种连接实际参数的手段,如果替换文本中的参数与##相邻,则参数将被实际参数替换,##与前后的空白符都将被删除,并对替换后的结果重新扫描。如:宏paste用于连接两个参数 #define paste(front, back) front ## back 宏调用paste(name, 1)的结果将建立记号name1,关于##详细参考附录A。 3.条件包含 还可以使用条件语句对预处理本身进行控制,这种条件语句的值是预处理执行的过程中进行计算。这种方式为在编译过程中根据计算所得的条件值选择性地包含不同代码提供了一种手段。 语句#if对其中的常量整型表达式(其中不能包含sizeof、类型转换运算符或enum常量)进行求值,若该表达式的值不等于0,则包含其后的各行,直到遇到遇到#endif、#elif或#else语句位置(预处理器语句#elif类似于else if)。在#if语句中可以使用表达式defined(名字),该表达式的值遵循下来规则:当名字已经定义时,其值为1;否则其值为0。 例如,为了保证hdr.h文件的内容只被包含一次,可以将该文件的内容包含在下列形式的条件语句中: #if !defined(HDR) #define HDR /* hdr.h文件的内容放在这里*/ #endif 第一次包含头文件hdr.h时,将定义名字HDR;此后再次包含该头文件时,会发现名字已经定义,这样就直接跳转到#endif处,类似的方法也可以用来避免多次重复包含统一文件。其中#if !defined(HDR)等价于#ifndef HDR。 下面这段代码首先测试系统变量SYSTEM,然后根据该变量的值确定包含哪个版本的头文件: #if SYSTEM == SYSV #define HDR "sysv.h" #elif SYSTEM == BSD #define HDR "bsd.h" #elif SYSTEM == MSDOS #define HDR "msdos.h" #else #define HDR "default.h" #endlif #include HDR PS: 最后还是希望文章对你有所帮助,强烈推荐大家阅读这本书,这篇文章主要是讲解了一些大家可能会忽略的C语言基础知识和经典知识。 (By:Eastmount 2015-10-20 深夜3点 http://blog.csdn.net/eastmount/)
这是一篇叙述自己在360公司参加笔试和面试的过程,可能面试的职位并不是你所学的方向,但是如果你能从中学到些什么或者吸取我的教训,那么作者就非常知足了。本着"学习别人是怎么失败的,活着出来的人才能成功"的目标,我从三个方面进行叙述: 第一部分:360公司笔试题 第二部分:面试过程 第三部分:注意事项及心得体会 同时,真心感谢360公司,我非常向往的一个公司。也非常感谢给我面试的那位大哥,让我真的学到了很多东西。所有题目版权归360所有,如果有不适的地方请告知我删除或修改。总之我认为:有的时候了解别人失败的案例比你总看别人成功的例子对你的帮助更大。看了下面我的本科毕设,你就会知道为什么我这么推崇这个公司了。 下载地址:http://download.csdn.net/detail/eastmount/8591789 一. 笔试题目 面试时间:2015年9月16日 面试岗位:PHP服务端开发工程师 职位要求:精通PHP/Python语言语法、掌握MySQL,基本了解Redis和MongoDB等各种DB、掌握HTML/CSS等。 题目难度:较难,选择题考得比较广,编程题一道极为简单一道比较复杂。 PS:因为当时题目都是一边做一边在草稿纸上抄的,可能有遗漏的地方,请海涵~ (一) 单选题: 1.MySQL存储过程的优点:某选项可以多次调用、修改,网络负载降低2.找出/etc/my.conf文件属于哪个包(package),执行: A.rpm -qf /etc/my.conf B.rpm -q /etc/my.conf C.rpm -q | grep /etc/my.conf D.rpm -requires etc/my.conf题解: 该题考察linux,答案是A。其中-f Query package owning FILE,赛马网: -ivh:安装显示安装进度--install--verbose--hash -Uvh:升级软件包--Update; -qpl:列出RPM软件包内的文件信息[Query Package list]; -qpi:列出RPM软件包的描述信息[Query Package install package(s)]; -qf:查找指定文件属于哪个RPM软件包[Query File]; -Va:校验所有的RPM软件包,查找丢失的文件[View Lost]; -e:删除包3.下面代码的运行结果: A.1 B.警告,没定义a::$myvar C.2 D.一个错误,没定义a::$myvar <?php class a{ function a($x=1) { $this->myvar=$x; } } class b{ var $myvar; function b($x=2) { $this->myvar=$x; parent::a(); } } $obj=new b; echo $obj->myvar; ?> 题解:答案A. 参考4.以下代码能正确显示图片的是: PS: 由于4段代码,仅仅手抄了B答案,仅供参考<?php header("content-type:image/jpeg"); $img=imagecreatefromjpeg("images/scce.jpg").imagejpeg($img); imagedestroy($img); ?> 5.下面的脚本输出值为多少: A.5 B.2 C.10 D.NULL<?php class my_class{ var $value; } $a=new my_class; $a->my_value=5; $b=$a; $b->my_value=10; echo $a->my_value; ?> 题解:通过 http://www.mcqyy.com/RunCode/php/ 运行结果为C.10 $b=$a的赋值,仅仅是建立一个链接,赋值后对$b的修改会影响$a。6.Person类实例化(new)一个对象$p,那使用对象$p调用Person类中getInfo方法: A.$p->getInfo() B.this->getInfo() C.$p::getInfo() D.$p=>getInfo()题解:A,常用的考察PHP类定义调用方法的问题。7.下列关于工厂方法factory叙述正确的是那个:8.R=(A,B,C)与SQL语句select distinct A from R where B=17等价关系代数表达式: A.πA(σB=17(R)) B.σB=17(πA(R)) C.σB=17(πA,C(R)) D.πA,C(σB=17(R))题解:其中select语句对应σB=17(R),而Select distinct A为消除重复,答案为A. 投影操作π是一个关系操作,所谓的出现重复行是指多个记录在投影属性上具有相同的取值,例如参考百度百科: 学号 姓名 性别 年龄 01 艾伦 男 17 02 三笠 女 17 03 阿明 男 17 在性别和年龄两个属性上投影后数据集只保留这两个属性列,结果如下: 性别 年龄 男 17 女 17 男 17 其中第一行和第三行就是重复行,虽然来自不同记录,但是这两个属性上的内容相同。需要消除相同的行(SQL语句默认不消除重复),最后结果就是: 性别 年龄 男 17 女 17 9.计算机cache,一主存层次采用相联映射方式,块大小为128字节,cache容量64块,按4块分组,主存容量为4096块,主存地址共需______位。 题解:由于主存容量为4096块,而每块为128个字,主存的总容量为512K字,故主存地址应为19位。主存地址应分为区号、组号、组内块号、块内地址号。可以看到,块内地址号应为7位,用以表示128个字。一组为4块,则组内块号用2位表示。Cache容量为64块共分16组,故组号需要用4位地址表示。剩余的即为区号,主存区号应为6位。|10.下列代码的运算结果为多少: var a = new Array(2,3,4,5,6); var sum=0; for(i=1;i<a.length;i++) sum+=a[i]; document.write(sum)题解:计算3+4+5+6=1811.document对象的是: A.form B.link C.三项都是 D.anchor答案:C12.mysql数据库还原命令是: 题解:恢复、备份数据库备份数据库shell > mysqldump -h host -u root -p13.求下列代码的时间复杂度,more than one answer is correct, choose the smallest one ( ). for(i=0; i<n; i++) for(j=1; j<=m; j*=2) for(z=j/2; z<j; z++) 其中某项答案:O(n*log(m)*m)14.HTML5库抛弃了: A.form B.applet C.frame D.center题解:html5不再使用fram,答案C。不再用frame、noframes和frameset,这些标签对可用性产生负面影响。HTML5中不支持frame框架,只支持iframe框架,或者用服务器方创建的由多个页面组成的符合页面的形式,删除以上这三个标签。 15.HTML5中,input元素type属性默认值为: A.search B.hidden C.text D.form题解:默认应该是text16.下列代码的输出结果是: A.24 B.17 C.72 D.36 d=lambda p:p*2 t=lambda p:p*3 x=2 x=d(x) x=t(x) x=d(x) print x 题解:感觉lambda表达式替换 2*2=4 4*3=12 12*2=24,应该输出A。Right?17.不是动态规划算法基本要素的是: A.马尔可夫性 B.建表填 C.运用子项叠代 D.最优子结构答案:A18.不要求最优子结构的是: A.分治法 B.贪心 C.动态规划 D.回溯答案:D19.下列叙述正确的是: PS:太长记不下来了 20.设有N堆沙子排成一排,其编号为1,2,3,…,N(N<=100)。每堆沙子有一定的数量。现要将N堆沙子并成为一堆。归并的过程只能每次将相邻的两堆沙子堆成一堆,这样经过N-1次归并后成为一堆。找出一种合理的归并方法,使总的代价最小: PS:答案没记录下来,但是这是典型的动态规划问题。 参考:http://blog.csdn.net/abcjennifer/article/details/580533021.下列不是分治法所能解决的问题特征是: A.子问题的解无后效性题解:只能怀疑答案是A,因为其它选项忘了,请见谅!分治法通常分为"分解-解决-合并"三个步骤,其中若干小规模的问题是可以解决的子问题,即具有最优子结构性质;最后将各个子问题的解合并为原问题的解。22.<div><a href="http://www.360.com">360</a></div>红色链接不正确的是: A.a:link{color:red}题解:该题考察网页基础知识,包括CSS定义超链接颜色等。A答案肯定正确,比较考察实际应用。23.下列不属于结构性伪类的是: A.E:root B.E:enabled C.E:first-child D.E:empty24.TCP连接,socket调用recv函数返回值为0表示: A.对端发送了一段长度为0的数据 B.对端关闭了连接 C.还没有收到对端数据 D.连接发生错误题解:答案B。如果recv函数在等待协议接收数据时网络中断了,那么它返回0。默认 socket 是阻塞的。阻塞与非阻塞recv返回值没有区分,都是: <0 出错 =0 连接关闭 >0 接收到数据大小。25.需对文件进行随机存取,下列哪种文件物理结构不适合上述应用场景? A.顺序文件 B.索引文件 C.链接文件 D.Hash文件题解:答案C。链式存储结构的存储地址不一定连续,无法通过计算地址实现随机访问,只能顺序访问。如果要随机访问的话只能顺序查找,效率低下。26.关于int *const ptr叙述正确的是: A.ptr不可修改,*ptr可修改 B.ptr可以修改,*ptr可修改 C.ptr可以修改,*ptr不可修改 D.ptr不以修改,*ptr不可修改题解:答案A。参考牛客网。const 的作用就是封锁它后面的东西,即后面的不可改变。 对于 int *const ptr 没有const关键字时为int* ptr,此时ptr是指向int的指针。加上const后,const修饰并封锁ptr ,即ptr的指向不可改变。 同理 int const* ptr(等同 const int *ptr) 。const修饰 * 解引用,即指针指向的内容不可改变。27.下列错误的用法是: PS:其中某个答案为 D.typedef void (*FUN)()28.下面代码fun(21)输出值多少: int fun(int a) { a^=(1<<5)-1; return a; }题解:首先(1<<5)表示左移5位,相当于1乘以2的5次方,即100000=32。 然后是异或运算: 21=010101 ^ 31=011111 =》 001010=10,结果为10。29.sort的template正确的写法是: A.void sort(class A first,class A last,class B pred) B.void template(class A,class B)sort(A first,A last,B pred) C.template<class A><class B> void sort(A first,A last,B pred) D.template<class A,class B> void sort(A first,A last,B pred)题解:参考牛客网,答案D。 函数模板的声明 模板函数格式是先声明模板类型,然后才能使用。 函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计。它的最大特点是把函数使用的数据类型作为参数。 函数模板的声明形式为: template<typename 数据类型参数标识符> <返回类型><函数名>(参数表) { 函数体 } 格式 template<class T1, class T2, ...> 返回值 函数名(参数列表){//函数体}30.16位机器,浪费多少空间? struct { char a; int b; char a; } A.8 B.4 C.6 D.2题解:答案D。16位机器,char型占1字节,int型占2个字节。数据自动对齐,实际结构体:1(char)+1(补齐)+2(int)+1(char)+1(补齐)=6字节,浪费2个字节空间。 参考我的博客:[C/C++基础知识] 面试再谈struct和union大小问题31.一道关于A[m][n]数组处理的题目: PS:答案好像有644、676、696等。32.关于C/C++宏定义错误的叙述是: A.宏定义不检查参数正确性,会有安全隐患 B.宏定义的常量更容易理解,如果可以使用宏定义常量的话,要避免使用const常量 C.宏的嵌套定义过多会影响程序的可读性,而且很容易出错 D.相对于函数调用,宏定义可以提高程序的运行效率题解:参考牛客网,答案B。参考:http://bbs.csdn.net/topics/340089467 使用const比使用define有一下几种好处: (1)const会进行数据类型检查,而define不会 (2)const存储在符号表\常量区,表示值不能修改33.下列值为多少:1^2^....^100 题解:答案100,python代码如下图所示: 34.下列关于继承错误的是: A.只能公有继承,不能私有继承 B.派生类可以访问基类protect成员 C.一个基类可以继承多个派生类,一个派生类可继承多个基类 D.基类中至少有一个虚函数可构成多态35.下列代码的输出值为多少: int main(int argc, char **argv) { int a[4] = {1, 2, 3, 4}; int *ptr = (int *)(&a + 1); printf("%d", *(ptr - 1)); } A.3 B.1 C.2 D.4题解:答案D。参考赛马网: 考察对于数组和指针的认识,指针加一的能力由类型决定。int*ptr=(int*)(&a+1); &a 和a 都指的是数组首元素的地址。不同的是a就是a+0 ,*(a+0)就是a[0],而&a+1相当于a[]数组类型的指针加1,此时指针加到数组的末尾。ptr接受后,由于Ptr的类型是int* 因此ptr-1即回退4字节。即指到最后一个元素。 36.下列可作为对象继承之间的转换的是: A.static_cast B.dynamic_cast C.const_cast D.reinterpret_cast题解:答案B。 dynamic_cast:在基类和派生类之间的转换,继承体系安全向下转型或跨系转型,找出某对象占用内存的起始点。static_cast:同旧式C转型,如int 到double。const_cast:常用于去除某个对象的常量性。reinterpret_cast 不具备移植性,常见用途是转化函数指针类型。37.下列是获得实例化对象所属类名字的函数是: A.get_class_methods() B.get_class() C.get_classname() D.get_object_vars()题解:答案B。PHP中没有get_classname()函数,其他如下: get_class — Returns the name of the class of an object get_object_vars — Gets the properties of the given object get_class_methods — Gets the class methods' names (二) 编程题: 1.计算器的新功能 可视化程序设计一个新功能的计算器,输入一个数时,能将这个数分解为一个或多个素因子乘积的形式,并按素因子的大小排列显示出来,0-9这十个数字表示如下:每个数字占5*3大小的字符区域。 输入:多组测试数n(n<=1,000,000) 输出:每个数分成若干个素数乘积形式,从小到大输出。素因子之间用"*"形式连接。 例: 输入: 10 2 输出: - - | | - * - | | - - - | - | - 首先需要计算素数组成,然后难点是怎样将数字一次性从上往下显示出来。 参考:http://972459637-qq-com.iteye.com/blog/22448242.研究生考试 政治100分,英语100分,数学150分,专业课150分。政治、英语要求单科不低于60分,数学、专业课要求单科不低于90分,总分不低于310分。总分350以上(含350)为公费,310-349分为自费。 请编程判断考生情况。 输入:正整数N,表示N组测试数据。每组4个正整数分:政治、英语、数学、专业 输出:Fail/Zifei/Gongfei 例: 3 61 62 100 120 80 80 120 100 55 90 130 130 输出: Zifei Gongfei FailPS:该题目比较简单,基本为送分题。同时希望该部分题目对你有所帮助!再次声明,此部分为我一边做题一边抄在纸上,所以有些遗漏的地方,请原谅~ 二. 面试过程 面试时间:2015年10月9日 面试部门:服务器端开发 面试地点:360大厦 面试时长:100多分钟 PS:过程中可能存在一些遗漏的地方,但是还是非常感谢那个面试的哥哥,今天都还觉得给人很舒服的感觉。同时因为间隔时间太长,最近也太忙,不准备采用对话方式进行,而是分几个步骤进行简单叙述。 第一部分 自我介绍 1.首先简单问候面试官并递上自己的简历,然后做个自我介绍; 2.面试官通过我的简历,让我介绍自己最拿得出手的项目,我介绍的是知识图谱相关的项目,包括:传统搜索引擎的工作原理、知识图谱概念(举例姚明身高、梁启超关系查询)、实体消歧与实体对齐、采用的VSM向量模型及聚类算法; 3.面试官问我该阶段主要熟悉什么语言?我说现在做得最多的是Python,以前是C/C++,当然Java、C#、PHP都做过,毕竟语言都有通性,但是想精通还是难。 4.他说PHP比较简单,他们是做底层服务器方向的,今天的面试主要是问Unix相关知识;我也赞同这个观点,因为PHP可以通过一些开源框架实现,同时也问了些自己做的WAMP网站。 第二部分 Unix为主 1.面试官首先问我是否做过Unix相关的东西?我说自己就简单做过Linux下的Python爬虫、脚本等。 2.然后问了Unix下的网络编程会不会?我简单介绍了Python的网络编程TCP\UDP的过程,主要的三次过程如下,同时Socket其他语言过程基本类似。 服务器: ss = socket() # 创建服务器套接字 ss.bind() # 地址绑定到套接字上 ss.listen() # 监听连接 inf_loop: # 服务器无限循环 cs = ss.accept() # 接受客户端连接 阻塞式:程序连接之前处于挂起状态 comm_loop: # 通信循环 cs.recv()/cs.send() # 对话 接受与发送数据 cs.close() # 关闭客户端套接字 ss.close() # 关闭服务器套接字 (可选) 客户端: cs = socket() # 创建客户端套接字 cs.connect() # 尝试连接服务器 comm_loop: # 通讯循环 cs.send()/cs.recv() # 对话 发送接受数据 cs.close() # 关闭客户端套接字 3.然后他又问如果客户端出现异常,服务器怎样捕获这个异常呢?我当时想了下,提出了服务器可以设置一个时间点(心跳),当某段时间没有接受到该客户端的报文,则表示断开连接或异常错误。他又问我能不能把这段recv()函数写出来。我说不太会。 PS:回来后想了想,当时是不是在考察recv()函数的返回值: >0获得报文长度, =0客户端断开连接,<0连接发生异常错误。但确实自己也不会Unix下的网络编程。 4.面试官又问了些Unix下的fork相关的知识,我说没有接触过。还有些英文不知道是什么,自己英语太差了~ 第三部分 算法和数据结构 1.面试官说:“你数据结构和算法应该很熟悉了吧!”我说:“还行,但是也忘记很多了。”自己确实很多基础知识都忘了很多,担心回答不上来。 2.面试官给我一张纸,有两段很长的代码(C语言),让我寻找两代码的区别。 这两段代码的主要区别就是参数一个是int,一个是double,当然前面还定义了些结构,代码里面的内容基本类似,相当于一个int型排序,一个double型排序。 他问我平时肯定会遇到这种情况,写两个函数代码过于冗余,怎样提炼成实现两种不同的类型排序,而且类型可以是float、结构体等等。 我说这有点类似于C++的模板啊!如果是C++就简单了,但是C语言主要是怎样判断这个类型呢? PS:后来回来想了想,感觉类似于qsort快速排序的那种写法,通过const void *a实现,不知道是不是。但有同学怀疑是不是考察##的连接用法。 int型快排 int cmp1(const void *a, const void *b) { return *(int*)a - *(int*)b; } qsort(num, len, sizeof(int), cmp1); double型快排 int cmp(const void *a, const void *b) { return *(double*)a > *(double*)b ? 1 : -1; } qsort(num, sum, sizeof(double), cmp); char型快排 int cmp(const void *a, const void *b) { return *(char*)a - *(char*)b; } qsort(str, sum, sizeof(char)*10, cmp); 3.上面代码没有写出来,那么你就做个最简单的吧!二叉树中序遍历非递归实现。 他又问我以前是怎么做的?我说通常都是三句话递归,这个题主要是考察通过栈模拟二叉树遍历递归的过程,然后写代码中。我失误了,栈写成队列了,然后队列是先进先出,又通过两个队列(一个输入队列、一个输出队列)模拟了一个栈实现了非递归遍历。 中序遍历:左孩子-根节点-右孩子,总体代码如下。参考 递归代码 void inOrder1(BinTree *root) //递归中序遍历 { if(root!=NULL) { inOrder1(root->lchild); cout<<root->data<<" "; inOrder1(root->rchild); } } 非递归遍历 根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下: 对于任一结点P, 1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理; 2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子; 3)直到P为NULL并且栈为空则遍历结束 void inOrder2(BinTree *root) //非递归中序遍历 { stack<BinTree*> s; BinTree *p=root; while(p!=NULL||!s.empty()) { while(p!=NULL) { s.push(p); p=p->lchild; } if(!s.empty()) { p=s.top(); cout<<p->data<<" "; s.pop(); p=p->rchild; } } } 如下图所示: 第四部分 结束面试 1.面试官问我最近在看什么书?我说《Python核心编程》和《Web数据挖掘》,然后问我有没有看过Unix的书籍,我说看过《Unix编程艺术》前两章。他就给我推荐了三本书,希望我回去看这三本,其他可以放一边了,那些都是小儿科了。 《Unix高级环境编程》《TCP\IP协议》《Unix网络编程》 PS:这三本数都是Unxi传奇W.Richard Stevens的作品,当时以为推荐书,有面试了100分钟以为有戏。可惜了,哈哈~ 2.然后又问我有什么问题,我说想自己实现个小的搜索引擎系统;他给我分析了硬件设备、分词、索引、倒排序、Rank、推荐系统等等知识。 3.最后让我出去等了大概15分钟左右,好像他们那天也比较忙,最后还是方向不太对口被拒了。但我自己已经非常知足了,一方面从他那学到了很多,另一方面也深深认识到了自己的不足,当初随便报了个PHP方向居然能面试,我也不知道报了这个方向。 三. 注意事项及心得体会 在最后总结之间,说点题外话。在回学校之前,因为360公司就在798艺术工厂的旁边,我去到那里逛了3个多小时。写下这样一段话: “今天早上来360面试,估计已跪,但仍不虚此行。会不会编程我不知道,但去了它旁边的798,发现自己还是有艺术细菌的。 好喜欢这种什么也不想,什么也不做,就静静地坐在角落,看着川流不息的人行的感觉。是那样的踏实惬意,那样的无忧无虑~ 因为不喜旅游,否则再忙也要出去看看这大千世界。但有时候又觉得做个井底之蛙也没有什么不好的,至少还可以每天看月亮。” ——Eastmount 最后简单总结下: 面试中经常考察的问题包括: 1.Socket套接字通信,TCP\UDP、同步异步解决方法; 2.基本算法和数据结构题目,包括二叉树遍历(含非递归)、快速排序(手写代码)、链表翻转等; 3.进程与线程的区别,是否写过线程相关的,如何解决同步、互斥等问题; 4.设计模式,如代理模式(图片浏览缩略图)、工厂模式、观察者模型等; 5.Cookie和Session的区别,缓存内容等,Android、前端开发常问; 6.后端开发常常会问Unix\Linux+C语言+网络通信的知识; 7.Python还会问爬虫、正则表达、开源框架Spider、docker、线程通信等; 8.自然语言处理会问分词、分类聚类、搜索引擎、推荐系统等; 提供几点建议: 1.申请职位一定要慎重考虑,一定是自己熟悉的东西,而不是各种职位都申请; 2.简历上的项目自己一定要熟悉,能说出项目的内容; 3.如果现在距离找工作早,建议尝试学习Linux、Unix下编程; 4.网络通信常常会问,简单的是TCP\UDP\Socket编程问题,三次握手等,如果深入就需要Unix相关知识; 5.算法很重要,如果有时间,一定要去做LeetCode题目,因为真的很多很多公司笔试面试题目都来自这里,比如链表翻转、字符串相似判断、二叉树遍历非递归等等; 6.如果有可能,尽量做些有深度的项目,简历中写"熟悉XXX语言、熟悉HTML或MySQL数据库",显然不如"对Linux下网络编程比较熟悉,通过Spider或XX框架进行过分布式爬取"。尽量让自己的简历更加专业、有水平。 7.如果有开源项目或者研究过源码、驱动这些最好不过,但显然很少人做到。 最后希望文章对你有所帮助,如果有不足或错误的地方,还请海涵~希望大家都找到自己心仪的工作。 作者写这篇文章也不容易,最近真心太忙了,明天还有好几个笔试面试,自己也报了几个贵州的大学,还是想回去任教啊!一生的梦想,不知在何方;但是不论深处何地,做什么工作,都需要不断地学习,保持一颗平常心和健康的身体去生活。哈哈,加油~ (By:Eastmount 2015-10-17 深夜2点半 http://blog.csdn.net/eastmount/)
很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send、Receive方法的同步通讯 Python网络编程也类似。同时最近找工作笔试面试考察Socket套接字、TCP\UDP区别比较多,所以这篇文章主要精简了《Python核心编程(第二版)》第16章内容。内容包括:服务器和客户端架构、套接字Socket、TCP\UDP通信实例和常见笔试考题。 最后希望文章对你有所帮助,如果有不足之处,还请海涵~ 一. 服务器和客户端架构 1.什么是客户端/服务区架构? 书中的定义是服务器是一个软件或硬件,用于向一个或多个客户端(客户)提供所需要的“服务”。服务器存在的唯一目的就是等待客户的请求,给这些客户服务,然后再等待其他的请求。而客户连接上(预先已知的)服务器,提出自己的请求,发送必要的数据,然后等待服务器完成请求或说明失败原因的反馈。 服务器不停的处理外来的请求,而客户一次只能提出一个服务的请求,等待结果。再结束这个事务。客户之后可以再提出其他的请求,只是这个请求会被视为另一个不同的事务了。 2.硬件客户端/服务器架构和软件客户端/服务器架构 硬件的客户端/服务器架构,例如打印服务器、文件服务器(客户可以远程把服务器的磁盘映射到自己本体并使用);软件客户端/服务器架构主要是程序的运行、数据收发、升级等,最常见的是Web服务器、数据库服务器。如一台机器存放一些网页或Web应用程序,然后启动服务。其服务器的任务就是接受客户端的请求,把网页发给客户端(如用户计算机上的浏览器),然后再等待下一个客户端请求。3.客户端/服务器网络编程 在完成服务之前,服务器必须要先完成一些设置。先要先创建一个通讯端点,让服务器能“监听”请求。你可以把我们服务器比作一个公司的接待员或回答公司总线电话的话务员,一旦电话和设备安装完成,话务员也就到位后,服务就开始了。 同样一旦通信端点创建好之后,我们在“监听”的服务器就可以进入它那等待和处理客户请求的无限循环中了。服务器准备好之后,也要通知潜在的客户,让它们知道服务器已经准备好处理服务了,否则没人会提请求的。所以需要把公司电话公开给客户。 而客户端只要创建一个通信端点,建立到服务器的连接,然后客户端就可以提出请求了。请求中也可以包含必要的数据交互。一旦请求处理完成,客户端收到了结果,通信就结束了。这就是客户端和服务器的简单网络通信。 二. 套接字Socket 1.什么是套接字 套接字是一种具有之前所说的“通信端点”概念的计算网络数据结构。相当于电话插口,没它无法通信,这个比喻非常形象。 套接字起源于20世纪70年代加州伯克利分校版本的Unix,即BSD Unix。又称为“伯克利套接字”或“BSD套接字”。最初套接字被设计用在同一台主机上多个应用程序之间的通讯,这被称为进程间通讯或IPC。 套接字分两种:基于文件型和基于网络的 第一个套接字家族为AF_UNIX,表示“地址家族:UNIX”。包括Python在内的大多数流行平台上都使用术语“地址家族”及其缩写AF。由于两个进程都运行在同一台机器上,而且这些套接字是基于文件的,所以它们的底层结构是由文件系统来支持的。可以理解为同一台电脑上,文件系统确实是不同的进程都能进行访问的。 第二个套接字家族为AF_INET,表示”地址家族:Internet“。还有一种地址家族AF_INET6被用于网际协议IPv6寻址。Python 2.5中加入了一种Linux套接字的支持:AF_NETLINK(无连接)套接字家族,让用户代码与内核代码之间的IPC可以使用标准BSD套接字接口,这种方法更为精巧和安全。 Python只支持AF_UNIX、AF_NETLINK和AF_INET家族。网络编程关注AF_INET。 如果把套接字比作电话的查看——即通信的最底层结构,那主机与端口就相当于区号和电话号码的一对组合。一个因特网地址由网络通信必须的主机与端口组成。 而且另一端一定要有人接听才行,否则会提示”对不起,您拨打的电话是空号,请查询后再拨“。同样你也可能会遇到如”不能连接该服务器、服务器无法响应“等。合法的端口范围是0~65535,其中小于1024端口号为系统保留端口。2.面向连接与无连接 面向连接:通信之前一定要建立一条连接,这种通信方式也被成为”虚电路“或”流套接字“。面向连接的通信方式提供了顺序的、可靠地、不会重复的数据传输,而且也不会被加上数据边界。这意味着,每发送一份信息,可能会被拆分成多份,每份都会不多不少地正确到达目的地,然后重新按顺序拼装起来,传给正等待的应用程序。 实现这种连接的主要协议就是传输控制协议TCP。要创建TCP套接字就得创建时指定套接字类型为SOCK_STREAM。TCP套接字这个类型表示它作为流套接字的特点。由于这些套接字使用网际协议IP来查找网络中的主机,所以这样形成的整个系统,一般会由这两个协议(TCP和IP)组合描述,即TCP/IP。 无连接:无需建立连接就可以通讯。但此时,数据到达的顺序、可靠性及不重复性就无法保障了。数据报会保留数据边界,这就表示数据是整个发送的,不会像面向连接的协议先拆分成小块。它就相当于邮政服务一样,邮件和包裹不一定按照发送顺序达到,有的甚至可能根本到达不到。而且网络中的报文可能会重复发送。 那么这么多缺点,为什么还要使用它呢?由于面向连接套接字要提供一些保证,需要维护虚电路连接,这都是严重的额外负担。数据报没有这些负担,所有它会更”便宜“,通常能提供更好的性能,更适合某些场合,如现场直播要求的实时数据讲究快等。 实现这种连接的主要协议是用户数据报协议UDP。要创建UDP套接字就得创建时指定套接字类型为SOCK_DGRAM。这个名字源于datagram(数据报),这些套接字使用网际协议来查找网络主机,整个系统叫UDP/IP。 3.socket()模块函数 使用socket模块的socket()函数来创建套接字。语法如下: socket(socket_family, socket_type, protocol=0) 其中socket_family不是AF_VNIX就是AF_INET,socket_type可以是SOCK_STREAM或者SOCK_DGRAM,protocol一般不填,默认值是0。 创建一个TCP/IP套接字的语法如下: tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 同样创建一个UDP/IP套接字的语法如下: udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 由于socket模块中有太多属性,所以使用"from socket import *"语句,把socket模块里面的所有属性都带到命名空间中,大幅缩短代码。调用如下: tcpSock = socket(AF_INET, SOCK_STREAM)4.套接字对象方法 下面是最常用的套接字对象方法: 服务器端套接字函数 socket类型 描述 s.bind() 绑定地址(主机号 端口号对)到套接字 s.listen() 开始TCP监听 s.accept() 被动接受TCP客户端连接,(阻塞式)等待连续的到来 客户端套接字函数 socket类型 描述 s.connect() 主动初始化TCP服务器连接 s.connect_ex() connect()函数扩展版本,出错时返回出错码而不是跑出异常 公共用途的套接字函数 socket类型 描述 s.recv() 接受TCP数据 s.send() 发送TCP数据 s.sendall() 完整发送TCP数据 s.recvfrom() 接受UDP数据 s.sendto() 发送UDP数据 s.getpeername() 连接到当前套接字的远端地址(TCP连接) s.getsockname() 获取当前套接字的地址 s.getsockopt() 返回指定套接字的参数 s.setsockopt() 设置指定套接字的参数 s.close() 关闭套接字 面向模块的套接字函数 socket类型 描述 s.setblocking() 设置套接字的阻塞与非阻塞模式 s.settimeout() 设置阻塞套接字操作的超时时间 s.gettimeout() 得到阻塞套接字操作的超时时间 面向文件的套接字函数 socket类型 描述 s.fileno() 套接字的文件描述符 s.makefile() 创建一个与套接字关联的文件对象 提示:在运行网络应用程序时,如果能够使用在不同的电脑上运行服务器和客户端最好不过,它能让你更好理解通信过程,而更多的是方位localhost或127.0.0.1. 三. TCP通信实例 1.服务器 tcpSerSock.py 核心操作如下: ss = socket() # 创建服务器套接字 ss.bind() # 地址绑定到套接字上 ss.listen() # 监听连接 inf_loop: # 服务器无限循环 cs = ss.accept() # 接受客户端连接 阻塞式:程序连接之前处于挂起状态 comm_loop: # 通信循环 cs.recv()/cs.send() # 对话 接受与发送数据 cs.close() # 关闭客户端套接字 ss.close() # 关闭服务器套接字 (可选) # -*- coding: utf-8 -*- from socket import * from time import ctime HOST = 'localhost' #主机名 PORT = 21567 #端口号 BUFSIZE = 1024 #缓冲区大小1K ADDR = (HOST,PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) #绑定地址到套接字 tcpSerSock.listen(5) #监听 最多同时5个连接进来 while True: #无限循环等待连接到来 try: print 'Waiting for connection ....' tcpCliSock, addr = tcpSerSock.accept() #被动接受客户端连接 print u'Connected client from : ', addr while True: data = tcpCliSock.recv(BUFSIZE) #接受数据 if not data: break else: print 'Client: ',data tcpCliSock.send('[%s] %s' %(ctime(),data)) #时间戳 except Exception,e: print 'Error: ',e tcpSerSock.close() #关闭服务器 2.客户端 tcpCliSock.py 核心操作如下: cs = socket() # 创建客户端套接字 cs.connect() # 尝试连接服务器 comm_loop: # 通讯循环 cs.send()/cs.recv() # 对话 发送接受数据 cs.close() # 关闭客户端套接字# -*- coding: utf-8 -*- from socket import * HOST = 'localhost' #主机名 PORT = 21567 #端口号 与服务器一致 BUFSIZE = 1024 #缓冲区大小1K ADDR = (HOST,PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) #连接服务器 while True: #无限循环等待连接到来 try: data = raw_input('>') if not data: break tcpCliSock.send(data) #发送数据 data = tcpCliSock.recv(BUFSIZE) #接受数据 if not data: break print 'Server: ', data except Exception,e: print 'Error: ',e tcpCliSock.close() #关闭客户端 3.运行结果及注意 由于服务器被动地无限循环等待连接,所以需要先运行服务器,再开客户端。又因为我的Python总会无法响应,所以采用cmd运行服务器Server程序,Python IDLE运行客户端进行通信。运行结果如下图所示: 如果出现错误[Error] Bad file descriptor表示服务器关闭客户端连接了,删除即可 建议:创建线程来处理客户端请求。SocketServer模块是一个基于socket模块的高级别的套接字通信模块,支持新的线程或进程中处理客户端请求。同时建议在退出和调用服务器close()函数时使用try-except语句。 四. UDP通信实例 1.服务器 udpSerSock.py 核心操作如下: ss = socket() # 创建服务器套接字 ss.bind() # 绑定服务器套接字 inf_loop: # 服务器无限循环 cs = ss.recvfrom()/ss.sendto() # 对话 接受与发送数据 ss.close() # 关闭服务器套接字 # -*- coding: utf-8 -*- from socket import * from time import ctime HOST = '' #主机名 PORT = 21567 #端口号 BUFSIZE = 1024 #缓冲区大小1K ADDR = (HOST,PORT) udpSerSock = socket(AF_INET, SOCK_DGRAM) udpSerSock.bind(ADDR) #绑定地址到套接字 while True: #无限循环等待连接到来 try: print 'Waiting for message ....' data, addr = udpSerSock.recvfrom(BUFSIZE) #接受UDP print 'Get client msg is: ', data udpSerSock.sendto('[%s] %s' %(ctime(),data), addr) #发送UDP print 'Received from and returned to: ',addr except Exception,e: print 'Error: ',e udpSerSock.close() #关闭服务器 2.客户端 udpCliSock.py 核心操作如下: cs = socket() # 创建客户端套接字 inf_loop: # 服务器无限循环 cs.sendto()/cs.recvfrom() # 对话 接受与发送数据 cs.close() # 关闭客户端套接字 # -*- coding: utf-8 -*- from socket import * HOST = 'localhost' #主机名 PORT = 21567 #端口号 与服务器一致 BUFSIZE = 1024 #缓冲区大小1K ADDR = (HOST,PORT) udpCliSock = socket(AF_INET, SOCK_DGRAM) while True: #无限循环等待连接到来 try: data = raw_input('>') if not data: break udpCliSock.sendto(data, ADDR) #发送数据 data,ADDR = udpCliSock.recvfrom(BUFSIZE) #接受数据 if not data: break print 'Server : ', data except Exception,e: print 'Error: ',e udpCliSock.close() #关闭客户端 3.运行结果及注意 UDP服务器不是面向连接的,所以不需要设置什么东西,直接等待连接就好。同时由于数据报套接字是无连接的,所以无法把客户端连接交给另外的套接字进行后续的通讯,这些服务器只是接受消息,需要的话加时间错后返回一个收到的结果给客户端。 UDP客户端与TCP客户端唯一区别就是不用去UDP服务器建立连接,而是直接把消息发送出去,然后等待服务器回复即可。 运行结果如下图所示:白色为客户端输入消息,黑色为服务器收到消息并回复。当Client输入"Hello, I am Client"时,服务器显示该消息并返回时间戳和收到的信息给客户端。 总结: 后面大家自己可以阅读下SocketServer模块,它是标准库中一个高级别的模块,用于简化实现网络客户端和服务器所需的大量样板代码。该模块中已经实现了一些可供使用的类直接调用几块。 Twisted框架是一个完全事件驱动的网络框架。它允许你使用和开发完全异步的网络应用程序和协议。 这些东西我更倾向于分享原理和底层的一些东西吧!同时最近考到的笔试题包括:TCP和UDP的区别、socket其中的参数含义、TCP三次握手及传递的参数、写个socket通讯伪代码。 总之,希望文章对你有所帮助~ (By:Eastmount 2015-10-5 早上8点 http://blog.csdn.net/eastmount/)
定义在函数内的变量有局部作用域,在一个模块中最高级别的变量有全局作用域。本文主要讲述全局变量、局部变量和导入模块变量的方法。 参考:《Python核心编程 (第二版)》 一. 局部变量 声明适用的程序的范围被称为了声明的作用域。在一个过程中,如果名字在过程的声明之内,它的出现即为过程的局部变量;否则出现即为非局部。例: def foo(x): print 'x = ',x x = 200 print 'Changed in foo(), x = ',x x = 100 foo(x) print 'x = ',x 输出结果如下:>>> x = 100 Changed in foo(), x = 200 x = 100 在主块中定义x=100,Python使用函数声明的形参传递x至foo()函数。foo()中把x赋值为200,x是函数的局部变量;所以在函数内改变x的值,主块中定义的x不受影响。 核心笔记: 当搜索一个标识符时,Python先从局部作用域开始搜索。如果在局部作用域内没有找到那个名字,那么一定会在全局域找到这个变量,否则会被抛出NameError异常。 作用域的概念和用于找到变量的名称空间搜索顺序相关。当一个函数执行时,所有在局部命名空间的名字都在局部作用域内;当查找一个变量时,第一个被搜索的名称空间,如果没有找到那个变量,那么就可能找到同名的局部变量。 二. 全局变量 全局变量的一个特征是除非删除掉,否则它们存活到脚本运行结束,且对于所有的函数,它们的值都是可以被访问的。然而局部变量,就像它们存放的栈,暂时地存在,仅仅只依赖于定义它们的函数现阶段是否处于活动。当一个函数调用出现时,其局部变量就进入声明它们的作用域。在那一刻,一个新的局部变量名为那个对象创建了,一旦函数完成,框架被释放,变量将会离开作用域。 X = 100 def foo(): global X print 'foo() x = ',X X = X + 5 print 'Changed in foo(), x = ',X def fun(): global X print 'fun() x = ',X X = X + 1 print 'Changed in fun(), x = ',X if __name__ == '__main__': foo() fun() print 'Result x = ',X 输出结果如下:>>> foo() x = 100 Changed in foo(), x = 105 fun() x = 105 Changed in fun(), x = 106 Result x = 106 核心笔记: 使用global语句定义全局变量。当使用全局变量同名的局部变量时要小心,如果将全局变量的名字声明在一个函数体内,全局变量的名字能被局部变量给覆盖掉。所以,你应该尽量添加global语句,否则会使得程序的读者不清楚这个变量在哪里定义的。 你可以使用同一个global语句指定多个全局变量。例如global x, y, z。 当我在制作Python爬虫时,需要想函数中传递url,循环爬取每个url页面的InfoBox,此时的文件写入操作就可以有两种方法实现:1.通过传递参数file;2.通过定义全局变量file。SOURCE = open("F:\\test.txt",'w') def writeInfo(i): global SOURCE SOURCE.write('number'+str(i)+'\n') def main(): i=0 while i<50: writeInfo(i) print i i=i+1 else: print 'End' SOURCE.close() main() PS:在此种用法中,如果我们在函数writeInfo()中不使用global 声明全局变量SOURCE,其实也可以使用,但是此时应该是作为一个内部变量使用,由于没有初始值,因此报错。Python查找变量是顺序是:先局部变量,再全局变量。UnboundLocalError: local variable 'SOURCE' referenced before assignment 三. 模块导入变量 主要方法是通过在py文件中模块定义好变量,然后通过import导入全局变量并使用。例: import global_abc def foo(): print global_abc.GLOBAL_A print global_abc.GLOBAL_B print global_abc.GLOBAL_C global_abc.GLOBAL_C = global_abc.GLOBAL_C + 200 print global_abc.GLOBAL_C if __name__ == '__main__': foo() print global_abc.GLOBAL_A + ' ' + global_abc.GLOBAL_B print global_abc.GLOBAL_C 输出如下所示,全局变量结构是可以改变的。>>> hello world 300 500 hello world 500 截图如下所示: PS:应该尽量避免使用全局变量。不同的模块都可以自由的访问全局变量,可能会导致全局变量的不可预知性。对全局变量,如果程序员甲修改了_a的值,程序员乙同时也要使用_a,这时可能导致程序中的错误。这种错误是很难发现和更正的。同时,全局变量降低了函数或模块之间的通用性,不同的函数或模块都要依赖于全局变量。同样,全局变量降低了代码的可读性,阅读者可能并不知道调用的某个变量是全局变量,但某些情况不可避免的需要使用它。 最后关于闭包和Lambda(相当于函数)就不再介绍,希望文章对你有所帮助~同时今天也是中秋节,祝所有程序猿和读者中秋节快乐。 (By:Eastmount 2015-9-27 下午4点 http://blog.csdn.net/eastmount/)
题目概述: Determine whether an integer is a palindrome. Do this without extra space. 题目分析: 判断数字是否是回文 例如121、656、3443 方法有很多,正着看和到着看两数相同;当然负数显然不是回文 我的方法: 第一种方法: 由于没有没有看到前面的without extra space。采用的方法是把数字转换为字符串,依次比较最前和最后两个字符是否相同,直到遍历完毕。 /** * 判断一个数字是否是回文数字 Palindrome * 最简单方法数组存储数字每位 判断回文 或依次比较数字left和right */ bool isPalindrome(int x) { char str[20]; //存储数字位数 int i,j; int count; //数字位数 //负数肯定不是回文 if(x<0) return false; i=0; while(x>0) { str[i]=x%10+'0'; i++; x=x/10; } count=i; i=0; j=count-1; while(i<j) { if(str[i]==str[j]) { i++; j--; } else { return false; } } return true; } 第二种方法: 通过依次比较最高位和最低位数字,然后依次数字降低两位。要点是先计算最高位是多少位。需要注意计算高位时:x=x-(left*result)例如110011 高位-1 低位-1 110011-100000=10011 10011/10=1001再判断,而%会出现错误。 /** * 判断一个数字是否是回文数字 Palindrome * without extra space. */ bool isPalindrome(int x) { int left,right; //数字高低位 int result; //计算高位数字 int number; if(x<0) return false; //计算最高位 result=1; number=x; while(number>=10) { //注意=10 result=result*10; number=number/10; } while(x>0) { left=x/result; //高位 right=x%10; //低位 if(left!=right) { return false; } else { //同时缩小两位 5005-5*1000=5 x=x-(left*result); //注意不能是 x=x%(left*result) 1100110011 Runtime Error result=result/100; x=x/10; } } return true; } Valid Palindrome Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. For example,"A man, a plan, a canal: Panama" is a palindrome. "race a car" is not a palindrome. 该题目主要是判断字符串是否是回文,其中仅仅是字母和数字组成,需要删除额外字符。注意:大小写字母可以相同'z'=‘Z’,大写字母转小写字母加32即可。判读一个字符串是否是回文,一种方法可以将字符串倒置然后和原字符串进行比较。这里采用一种类似字符串翻转的方法,通过从前后两个方向来比较判断是否是回文。自己AC感觉还行,代码如下: //自定义函数 判断是否是数字或字母 bool IsNumberAlp(char ch) { if( (ch<='Z'&&ch>='A') || (ch<='z'&&ch>='a') || (ch<='9'&&ch>='0') ) { return true; } else { return false; } } //字符串回文判断 包括字母和数字 "aA" 返回True说明不区分大小写 bool isPalindrome(char* s) { int length; //字符串长度 int i,j; char left,right; //比较两个字符 //s输出true "" if(s==NULL) return true; length=strlen(s); //数组下标i从前往后遍历 j从后往前遍历 i=0; j=length-1; left=0; right=0; while(i<=j) { if(IsNumberAlp(s[i])==true) { //字母 数字 left = s[i]; if(left>='A'&&left<='Z') { //大写字母转小写字母加32(重点) left=left+32; } } else { i++; continue; } if(IsNumberAlp(s[j])==true) { right = s[j]; if(right>='A'&&right<='Z') { right=right+32; } } else { j--; continue; } if(left!=right) { return false; } else { i++; j--; } } return true; } 最后希望文章对你有所帮助,如果文章中有错误或不足之处,还请海涵~ (By:Eastmount 2015-9-24 凌晨4点 http://blog.csdn.net/eastmount/)
最近找工作参加了很多笔试,其中考察结构体和联合体的大小问题是经常出现的一个问题。虽然题目简单而且分值比较低,但是还是想再给大家回顾下这些C和C++的基础知识。希望文章对你有所帮助~ PS:意外惊喜第三部分,所有权归它们公司所有。我只想分享学习并无它,望海涵~ 一. 真题介绍 1.[2015-9 完美] 在IA32架构下,下面的union结构的sizeof大小为:_______ union PageLayout { struct { int page_index; char key[5]; }; char dummy[10]; }; 题解: 该题充分考察了结构struct和union联合的区别: 联合与结构的本质区别在于内存使用方式的不同。 结构中不同的成员使用不同的存储空间,一个结构所占的内存大小是结构中每个成员所占内存大小的总和,结构体中每个成员相互独立,是不能占用同一存储单元的。 联合大小取决于其中最大的数据类型内存分配大小,联合中内存是叠加存放的。 同一存储区域由不同类型的变量共享,这种数据类型就是联合(也称共同体)。 故:联合PageLayout中由struct和dummy[10]两部分组成,其中结构的大小为int型4字节+char8字节,因为它需要字节对齐为4的整数倍,结构12字节+dummy字符数组10字节。 答案:12 2.[2013 完美世界] 求结构st的大小为_______. struct st { char ch,*ptr; union { short a,b; unsigned int c:2,d:1; }; bool f; struct st *next; }; 题解: 该提醒主要考察结构体中嵌套联合的大小,原理同上。其中int型为4字节,short为2字节,指针相当于无符号的整形4字节,同时补齐则为实际大小。 char ch内存对齐后为4字节、char类型指针4字节、union为4字节(其中联合为最大数据类型的内存大小,short a 2字节、short b 2字节、无符号Int4字节,位域问题见下)、bool型补齐4字节、struct类型指针4字节,总共占:4+4+4+4+4=20字节。 答案:20 位域:把一个字节中的二进制位划分为几个不同的区域,并说明每个区域的位数。 格式:类型说明符 位域名:位域长度,例如 unsigned int c:2表示c在内存中占2位 通过如下程序输出结构中内存地址如下图所示: 3.[变形 完美] 求结构st的大小为_______. struct st { char ch,*ptr; union A { short a,b; unsigned int c:2,d:1; }; bool f; struct st *next; }; 题解: 它与上题的区别联合声明A,表示定义的一个类型不用占用内存;而如果没有声明A,则表示声明了结构体中的一个成员,需要占内存。 内存大小为ch补齐4字节、char指针4字节、联合不占内存0字节、bool型补齐4字节、结构指针4字节,输出16字节。注意:如果联合后面添加ui,如 "union A{....}ui;" 此时输出结果为20。 答案:16 二. 其他题型 代码分别如下所示: struct A { int page_index; char key[5]; }; 输出:12 其中int4字节+char两个4字节补齐 struct B { char a; int b; double c; }; 输出16 其中补齐4字节+int型4字节+double8字节,其中为什么补齐4而不是8呢?思考下~ struct C { char a; double c; char b; }; 输出:24 输出补齐a8字节+double8字节+c补齐8字节union { long i; int k; char c; char s[4]; }D; 输出:4 联合4字节,其中由于联合各个成员使用共同的存储区域,当向其中一个成员赋值时,联合中的其他值也会发生变化。 推荐文章:http://blog.chinaunix.net/uid-26943148-id-3196468.html 三. 其他经典考题 由于很多题目都要求不能泄露,只能凭借记忆简单再分享几种常见的题型,这些基础型题目是关于C\C++\数据结构的,任何岗位都可能遇到。因为我个人报的岗位众多,包括:C++开发、算法工程、NLP、PHP开发、大数据方向等,但是还是建议: "精>>杂 | 专一>>博爱 | LeetCode>>不做" 这么多笔试,让我牢记一点:山外有山,人外有人,尤其是程序猿,自己真心太弱,要学习的东西太多太多;但是什么时候都不能丢失自我和自己感兴趣的东西,即使再累再苦,做自己喜欢的东西就是幸福,比如写博客、玩爬虫、赏美文、学习新知识。即使半夜凌晨,分享一篇博客或看到好的东西都让人欣喜,这就是生活吧! 1.[2015-9 完美] int n=0; while(n=1) n++; while循环执行的次数是:______. 答案:无限循环 因为while(n=1)是个赋值语句,表示动作始终为true 2.[2015-9 完美] 二叉树后序遍历序列为DEBFCA,中序遍历序列为DBEAFC,则前序遍历顺序:______. 提示:E代价也考察了类似题目 先序表示根->左->右、中序表示左->根->右、后序表示左->右->根。记住先序根在前面,后序根在最后。 题中后序DEBFCA,显然A为第一个根节点。前序最先输出A 3.[2015-9 完美] 下面程序的输出是多少:_____. #define add(a+b) a+b int main() { printf("%d\n",5 * add(3+4)); return 0; } 答案:19 这是一道非常基础的考察宏定义的题目,题目是错的,应该改为add(a,b)。但显然不影响其功能,它替换后结果为:5*3+4=19,易错误的结果是输出35。显然它没有添加括号。再补充一道2015后端研发美团的类似题目。 4.[2015-9 美团] 多个源文件组成C程序,经过编辑、预处理、编译、链接生成可执行程序,下列哪个可以发现被调用的函数未定义? 答案:链接 解析:本题考查的是程序编译过程的基本知识。对于编译型程序设计语言C,在程序编写完成后执行前,主要进行预处理、翻译为目标代码和链接库函数等关键步骤。 在这三步中,预处理分析程序中的宏定义并替换宏引用,翻译主要针对一个编译单元(通常对应一个源文件)进行,将该编译单元翻译为中间代码,链接过程将各个编译单元中变量和函数的引用与其定义绑定,确保程序中使用的所有变量和函数都存在对应实体。所以,未定义的函数引用只能在链接过程中发现。 5.[2015-9 美团] 按入栈序列式ABCDE,不可能出栈的序列式:_______. DECBA DCEBA ECDBA ABCDE 提示:该题目完美、E代价等公司都有 答案:ECDBA 经典的考察出栈题目: 如果在草稿纸上画出入栈图就非常容易了。此时入栈ABCD D => E C => C B => B A => A 出D人E再出CBA,出DC入E再出BA,最后是入一个出一个,而E出后必须先D后C,故ECDBA错误。 6.二分查找第一轮查找的关键字是什么?快速排序第一轮结果是什么?堆排序第一轮后的结果是什么?(多为选择题) 7.[2015-9 360] Person类实例化new一个对象$p,那如何使用对象$p调用Person类中的方法: A.$p->getInfo() B.this->getInfo() C.$p::getInfo() D.$p=>getInfo() 提示:PHP方向 完美考了面向对象的继承,不能直接访问基类中继承的某个成员,通常是私有成员;内敛函数、虚函数等知识。 8.[2015-9 360] 下列不是动态规划算法的基本要素? A.马尔科夫性 B.建表填充 C.子问题叠代 D.最优子结构 9.[2015-9 360] 下列不要求最优子结构的: A.分治法 B.贪心算法 C.动态规划 D.回溯法 提示:贪心算法和动态规划的共同点就是最优子结构。 10.[2015-9 360] TCP连接socket上调用recv函数,返回值为0表示: A.对端关闭连接 B.连接错误 C.对端发送长度为0数据 D.还没收到对端数据 怀疑:A TCP面向连接,保证数据安全、三次握手;UDP包可能丢失,但速度更快。(完美) 套接字编程send和recv参数也常考。recv的功能是从接收缓冲区读取(其实就是拷贝)指定长度的数据。recv返回的条件有两种: (1). recv函数传入的应用层接收缓冲区已经读满 (2). 协议层接收到push字段为1的TCP报文,此时recv返回值为实际接收的数据长度 客户端的程序连接上服务器后recv函数阻塞接受,有时会返回0,说明接收超时服务器主动断开了连接,需要重新connect服务器。参考 E代价有道题目也比较好: 200-OK 请求成功 400-Bad Request 语义有无,当前请求无法被服务器理解 403-Forbidden 服务器已经理解请求,但拒绝执行它 404-Not Found 请求失败,请求资源未被服务器发现 500-Interanl Server Error 服务器遇一个未曾预料的状况,导致无法完成请求处理 505-HTTP Version Not Support 服务器不支持或拒绝在请求中使用HTTP版本 11.数据库常考select语句、事务ACID 12.操作系统就是死锁及解决死锁方法、进程线程区别、阻塞执行就绪状态的天下 最后为什么说leetcode远远大于不做,因为最开始我挺反感A题的,后来实在是后悔A晚了。毕竟不是三年前疯狂刷题的我,同时很多题目都是看不出BUG的;尤其是笔试中很多Leetcode题目。包括: 完美的链表转置Reverse、E代价的判断单链表是否存在环(一步两步问题)、美团的循环n=n&(n-1)计算n二进制中1的个数、360的求素数、阿里巴巴以对的形式判断最大堆结合二叉树、掌趣游戏二分查找、二叉树层次遍历(队列)、安全线性队列类等。 总之,希望文章对你有所帮助吧!还是低调点,都不敢发布出来。嘘~ (By:Eastmount 2015-9-23 凌晨4点 http://blog.csdn.net/eastmount/)
题目概述: Description:Count the number of prime numbers less than a non-negative number, n. 解题方法: 题意是给出n中所有素数的个数。 首先你需要知道判断一个数是不是素数的方法:(最笨方法但有效) bool IsPrime(int n) { if (n<2) { //小于2的数即不是合数也不是素数 return false; } //和比它小的所有的数相除,如果都除不尽则证明素数 for(int i=2; i<n; i++) { if (n%i==0) { return false; //除尽则是合数 } } return true; } 更多数学方面关于素数判断问题,参考博客:算法总结:判断一个数是否为素数 最初想法是通过两层循环依次判断n个数里素数个数,但代码显然会TLE。当n=499979时就Time Limit Exceeded,超时代码如下:int countPrimes(int n) { int count=0; int i,j; int number; if(n<=1) return 0; for(i=2; i<n; i++) { //分别判断i是不是素数 number = i; for(j=2; j<i; j++) { if(number%j==0) { //除尽表示不是素数 break; } } count++; } return count; } 后来找到一种方法,只能说是Perfect。它叫做Sieve of Eratosthenes(埃拉托色尼筛法,简称埃氏筛法),参考维基百科:Sieve of Eratosthenes 它的思想是通过建立一个bool型数组,从2开始计数,其倍数的数字都赋值为true,显然不是素数。最后统计false的个数即为素数个数。我的代码: /** * 题意:计算n中的素数个数 * <span style="font-family: Arial, Helvetica, sans-serif;">第二种方法 Sieve of Eratosthenes 埃拉托色尼筛法,简称埃氏筛法</span> * By: Eastmount 2015-9-21 */ int countPrimes(int n) { int count; int i,j; bool *nums; if(n<=1) return 0; //动态数组nums记录是否为素数 nums = (bool*)malloc(sizeof(bool)*n); for(i=2; i<n; i++) { //i的倍数均非素数 if(!nums[i]) { for(j=2; j*i<n; j++) { nums[j*i] = true; } } } //计算素数个数 count = 0; for(i=2; i<n; i++) { if(nums[i]==false) { count++; } } free(nums); return count; } 类似题目: Ugly Number Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 6, 8 are ugly while 14 is not ugly since it includes another prime factor 7. Note that 1 is typically treated as an ugly number. 题意:就是子数由2、3、5组成的数字即为Ugly数字,6=2*3、8=2*2*2。 bool isUgly(int num) { //丑数具有如下特征:1是丑数,丑数可以表示为有限个2、3、5的乘积 int result=0; if(num<=0) //防止Last executed input:0 超过时间限制 return false; while(num!=1) { if(num%2==0) { num=num/2; } else if(num%3==0) { num=num/3; } else if(num%5==0) { num=num/5; } else { result=1; break; } } if(result==1) return false; else return true; } 该方法真的好友技巧啊!只能说算法真是博大精深,同时最近360的笔试题目也考到了一个数字由两个素数组成并打印成5*3的图形题目。所以还是挺重要的知识。 (By:Eastmount 2015-9-21 凌晨3点半 http://blog.csdn.net/eastmount/)
题目概述: Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the characters in s can be replaced to get t. All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character but a character may map to itself. For example, Given "egg", "add", return true. Given "foo", "bar", return false. Given "paper", "title", return true.Note: You may assume both s and t have the same length.解题方法: 该题意是判断两个字符串s和t是否是同构的。(注意字符不仅是字母) 最简单的方法是通过计算每个字符出现的个数并且相应位置字母对应,但是两层循环肯定TLE。所以需要通过O(n)时间比较,采用的方法是: eg: "aba" <> "baa" return false 关键代码:nums[s[i]]=t[i] numt[t[i]]=s[i] 再比较是否相同 nums['a']='b' numt['b']='a' (第一次出现) nums['b']='a' numt['a']='b' (第一次出现) nums['a']='b' <> t[2]='a' (第二次出现) return false 该方法技巧性比较强,当然如果你使用C++的映射就非常容易实现了。 我的代码: bool isIsomorphic(char* s, char* t) { int ls,lt; //字符串长度 int i,j; int nums[256]={0}; int numt[256]={0}; ls = strlen(s); lt = strlen(t); if(ls!=lt) return false; for(i=0; i<ls; i++) { //初值为0 if(nums[s[i]]==0) { if(numt[t[i]]==0) { nums[s[i]] = t[i]; numt[t[i]] = s[i]; } else { return false; } } else { if(nums[s[i]]!=t[i]) { return false; } } } return true; } C++推荐代码: 参考:http://www.cnblogs.com/easonliu/p/4465650.html 题目很简单,也很容易想到方法,就是记录遍历s的每一个字母,并且记录s[i]到t[i]的映射,当发现与已有的映射不同时,说明无法同构,直接return false。但是这样只能保证从s到t的映射,不能保证从t到s的映射,所以交换s与t的位置再重来一遍上述的遍历就OK了。 class Solution { public: bool isIsomorphic(string s, string t) { if (s.length() != t.length()) return false; map<char, char> mp; for (int i = 0; i < s.length(); ++i) { if (mp.find(s[i]) == mp.end()) mp[s[i]] = t[i]; else if (mp[s[i]] != t[i]) return false; } mp.clear(); for (int i = 0; i < s.length(); ++i) { if (mp.find(t[i]) == mp.end()) mp[t[i]] = s[i]; else if (mp[t[i]] != s[i]) return false; } return true; } }; (By:Eastmount 2015-9-21 凌晨1点半 http://blog.csdn.net/eastmount/)
目录:1.Move Zeroes - 数组0移到末尾 [顺序交换] 2. 一.Move Zeroes 题目概述:Given an arraynums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.For example, givennums = [0, 1, 0, 3, 12], after calling your function,nums should be[1, 3, 12, 0, 0].Note: 1.You must do this in-place without making a copy of the array. 2.Minimize the total number of operations. 解题方法:题意是把数组nums中0的元素后置,同时不能采用赋值数组。两种方法: 1.遇到是0的元素从数组最后向前存储并移位,遇到非0元素从前存储; 2.推荐:从前往后查找,不是0的元素前移,并计算0的个数,后面的全置0。我的代码:方法一:Runtime: 28 ms void moveZeroes(int* nums, int numsSize) { int endNum; //从后计数0 int startNum; //从前计数非0 int temp; int i,j; i = 0; startNum = 0; endNum = 0; while( (i+endNum) < numsSize ) { if(nums[i]==0) { //依次前移 for(j=startNum; j<numsSize-endNum-1; j++) { //j少一个数 nums[j] = nums[j+1]; } nums[numsSize-endNum-1] = 0; endNum++; } else { nums[startNum] = nums[i]; startNum++; i++; } } } 方法二:Runtime: 8 msvoid moveZeroes(int* nums, int numsSize) { int count; //计算0的个数 int i,j; int n; n = 0; count = 0; for(i=0; i<numsSize; i++) { if(nums[i]==0) { count++; } else { nums[n] = nums[i]; n++; } } //后置0 for(j=0; j<count; j++) { nums[n] = 0; n++; } } (By:Eastmount 2015-9-20 晚上8半 http://blog.csdn.net/eastmount/)
通常测试人员或公司实习人员需要处理一些txt文本内容,而此时使用Python是比较方便的语言。它不光在爬取网上资料上方便,还在NLP自然语言处理方面拥有独到的优势。这篇文章主要简单的介绍使用Python处理txt汉字文字、二维列表排序和获取list下标。希望文章对你有所帮助或提供一些见解~ 一. list二维数组排序 功能:已经通过Python从维基百科中获取了国家的国土面积和排名信息,此时需要获取国土面积并进行排序判断世界排名是否正确。 列表基础知识 列表类型同字符串一样也是序列式的数据类型,可以通过下标或切片操作来访问某一个或某一块连续的元素。它和字符串不同之处在于:字符串只能由字符组成而且不可变的(不能单独改变它的某个值),而列表是能保留任意数目的Python对象灵活容器。 总之,列表可以包含不同类型的对象(包括用户自定义的对象)作为元素,列表可以添加或删除元素,也可以合并或拆分列表,包括insert、update、remove、sprt、reverse等操作。 列表排序介绍 常用列表排序方法包括使用List内建函数list.sort()或序列类型函数sorted(list)排序#list.sort(func=None, key=None, reverse=False) list = [4, 3, 9, 1, 5, 2] print list list.sort() print list #输出 [4, 3, 9, 1, 5, 2] [1, 2, 3, 4, 5, 9] 通过对比下面的代码,可以发现两种方法的区别是:list.sort()改变了原list的顺序,而sorted没有。#sorted(list) list = ['h', 'a', 'p', 'd', 'i', 'b'] print list print sorted(list) print list #输出 ['h', 'a', 'p', 'd', 'i', 'b'] ['a', 'b', 'd', 'h', 'i', 'p'] ['h', 'a', 'p', 'd', 'i', 'b'] 二维列表排序 通过lambda表达式实现二维列表排序,并且按照第二个关键字进行排序。参考#list.sort(func=None, key=None, reverse=False) list = [('Tom',4),('Jack',7),('Daly',9),('Mary',1),('God',5),('Yuri',3)] print list list.sort(lambda x,y:cmp(x[1],y[1])) print list #输出 [('Tom', 4), ('Jack', 7), ('Daly', 9), ('Mary', 1), ('God', 5), ('Yuri', 3)] [('Mary', 1), ('Yuri', 3), ('Tom', 4), ('God', 5), ('Jack', 7), ('Daly', 9)] 题目中如果第一个数存储文件中读取的行号,第二个数存储人口数量,此时可对第二个数进行排序。需要注意的是它们一组(1,93)是tuple元组。#list.sort(func=None, key=None, reverse=False) list = [(1,93),(2,71),(3,89),(4,93),(5,85),(6,77)] print list list.sort(key=lambda x:x[1]) print list #输出 [(1, 93), (2, 71), (3, 89), (4, 93), (5, 85), (6, 77)] [(2, 71), (6, 77), (5, 85), (3, 89), (1, 93), (4, 93)] lambada表达式 在上述代码中,如果还不知道lambada是什么鬼东西的话?那我就来帮你回顾了。 python允许使用lambda关键字创造匿名函数,它不需要以标准的方式来声明,如def语句。然而作为函数,它们也能有参数。 lambda就是一个表达式,而不是一个代码块。而且这个表达是的定义必须和声明放在同一行,能在lambda中封装有限的逻辑进去,起到一个函数速写的作用。例如:#lambda [arg1[, arg2, ..., argN]]:expression f = lambda x,y,z:x+y+z num = f(1,2,3) print 'lambda: ' + str(num) #等价于 def add(x,y,z): return x+y+z num = add(1,2,3) print 'function: ' + str(num) #输出 lambda: 6 function: 6 二. 处理txt文本 下面是通过txt文件按行读取,并获取面积进行排序。其中核心代码如下: 读取文件&列表添加 source = open("F:\\Student\\1Area.txt",'r') lines = source.readlines() L = [] #列表二维 国家行数 人口数 count = 1 #当前国家在文件中第count行 for line in lines: line = line.rstrip('\n') #去除换行 .... #获取排名和面积 fNum = string.atof(number) #面积 L.append((count,ffNum)) #列表添加 count = count + 1 else: print 'End While' source.close() 列表排序 L.sort(lambda x,y:cmp(x[1],y[1]),reverse = True) #遍历过程 表示第i名 (文件第x行,面积y平方公里) #重点 L[i]输出列表 1 (46, 17075200.0) L[i][0]表示元组tuple第一个数 1 46 for i in range(len(L)): print (i+1), L[i] 获取面积字符串line = line.rstrip('\n') #去除换行 start = line.find(r'V:') end = line.find(r'平方公里') number = line[start+2:end] number = number.replace(',','') #去除',' #输出 line => C:国家 E:中华人民共和国 A:国土面积 V:9,634,057或9,736,000平方公里(世界第3/4名) number => 9634057或9736000 最后同时需要处理各种字符串情况,如‘或’、‘万’要乘10000、删除‘[1]’等。更简单的方法是通过正则表达式或获取第一个非数字字符。 运行结果如下所示,排序后的txt和纠错txt: 代码如下: # coding=utf-8 import time import re import os import string import sys source = open("F:\\Student\\1Area.txt",'r') lines = source.readlines() count = 1 L = [] #列表二维 国家行数 人口数 ''' 第一部分 获取国土面积 ''' print 'Start!!!' for line in lines: line = line.rstrip('\n') #去除换行 start = line.find(r'V:') end = line.find(r'平方公里') number = line[start+2:end] number = number.replace(',','') #去除',' fNum = 0.0 if '万' in number: end = line.find(r'万') newNum = line[start+2:end] fNum = string.atof(newNum)*10000 else: #如何优化代码 全局变量 if '/' in number: end = line.find(r'/') newNum = line[start+2:end] newNum = newNum.replace(',','') fNum = string.atof(newNum) elif '(' in number: end = line.find(r'(') newNum = line[start+2:end] newNum = newNum.replace(',','') fNum = string.atof(newNum) elif '[' in number: end = line.find(r'[') newNum = line[start+2:end] newNum = newNum.replace(',','') fNum = string.atof(newNum) elif '或' in number: end = line.find(r'或') newNum = line[start+2:end] newNum = newNum.replace(',','') fNum = string.atof(newNum) elif ' ' in number: end = line.find(r' ') newNum = line[start+2:end] newNum = newNum.replace(',','') fNum = string.atof(newNum) else: fNum = string.atof(number) #print line #print number #print fNum L.append((count,fNum)) count = count + 1 else: print 'End While' source.close() ''' 第二部分 从大到小排序 参看 http://blog.chinaunix.net/uid-20775448-id-4222915.html ''' L.sort(lambda x,y:cmp(x[1],y[1]),reverse = True) #print L #遍历过程 表示第i名 (文件第x行,面积y平方公里) #重点 L[i]输出列表 1 (46, 17075200.0) L[i][0]表示元组tuple第一个数 1 46 for i in range(len(L)): print (i+1), L[i] ''' 第三部分 读写文件 ''' source = open("F:\\Student\\1Area.txt",'r') lines = source.readlines() result = open("F:\\Student\\1NewArea.txt",'w') count = 1 for line in lines: line = line.rstrip('\n') #获取列表L中排名位置pm pm = 0 for i in range(len(L)): if count==L[i][0]: pm = i+1 break #获取文件中名次 if '世界第' in line: start = line.find(r'世界第') end = line.find(r'名') number = line[start+9:end] if '/' in number: #防止中国第3/4名 end = line.find(r'/') number = line[start+9:end] if '包括海外' in number: number = '41' print number,pm,type(number),type(pm) if string.atoi(number)==pm: line = line + ' 【排名正确】 【世界第' + str(pm) + '名】' result.write(line+'\n') else: line = line + ' 【排名错误】 【世界第' + str(pm) + '名】' result.write(line+'\n') else: #文件中没有排名 line = line + ' 【新加排名】 【世界第' + str(pm) + '名】' result.write(line+'\n') count = count + 1 else: print 'End Sorted' source.close() result.close() ''' 第四部分 输出一个排序好的文件 便于观察 ''' source = open("F:\\Student\\1Area.txt",'r') lines = source.readlines() result = open("F:\\Student\\1NewSortArea.txt",'w') #i表示第i名 L[i][0]表示行数 pm = 0 for i in range(len(L)): pm = L[i][0] count = 1 for line in lines: line = line.rstrip('\n') if count==pm: line = line + ' 【世界第' + str(i+1) + '名】' result.write(line+'\n') break else: count = count + 1 else: print 'End Sorted Second' source.close() result.close() 最后希望文章对你有所帮助,文章主要通过讲述一个实际操作,帮你巩固学习liet列表的二维排序和字符串txt处理。如果文中有错误或不足之处,还请海涵~ (By:Eastmount 2015-9-16 晚上9点 http://blog.csdn.net/eastmount/)
题目概述: Reverse a singly linked list. 翻转一个单链表,如:1->2 输出 2->1;1->2->3 输出3->2->1。 题目解析: 本人真的比较笨啊!首先想到的方法就是通过判断链尾是否存在,再新建一个链表,每次移动head的链尾元素,并删除head链表中的元素,一个字“蠢”,但好歹AC且巩固了链表基础知识。你可能遇见的错误包括: 1.'ListNode' undeclared (first use in this function) nhead=(istNode*)malloc(sizeof(ListNode)); => nhead=(struct ListNode*)malloc(sizeof(struct ListNode)); 2.Time Limit Exceeded 在链表遍历寻找最后一个结点并插入新链表尾部中需要注意,建议的方法: q=head; while(q) {q=q->next;} p=(struct ListNode*)malloc(sizeof(struct ListNode)); p->val=head->val; p->next=NULL; q=p; => q=head; while(q) {last=q; q=q->next;} p=(struct ListNode*)malloc(sizeof(struct ListNode)); p->val=head->val; p->next=NULL; last->next=p; 通过借助last变量更直观,否则结果总是错误。而且此时q为next指向NULL,如果用到q->next=p就会出现RE错误,因为q都为NULL,哪来的q->next。第二个错误也可能是我个人的编程习惯吧! 第二种方法更为推荐——直接翻转,还有一种递归方法自行提高。 如下图所示,红色表示初始链表存在4个值[1, 2, 3, 4],蓝色表示初始指针first指向第一个元素、second指向第二个元素(head->next),third指向第三个元素;首先s->next=f断开链表并翻转指向第一个元素,同时f=s最后返回first。如果只有两个元素[1, 2]则执行"s->next=f; f=s;"后s=t=NULL返回f即可输出[2, 1]。 我的代码: 直接翻转方法/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* reverseList(struct ListNode* head) { struct ListNode *first,*second,*third; if(head==NULL||head->next==NULL) return head; first = head; second = head->next; first->next = NULL; while(second!=NULL) { //注意while(second)不能执行 third = second->next; second->next = first; first = second; second = third; } return first; } "蠢"方法/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ //个人思路:判断链尾是否存在 翻转到一个新链表 struct ListNode* reverseList(struct ListNode* head) { struct ListNode *nhead,*q,*p,*last,*nq,*np; int value; if(head==NULL||head->next==NULL) return head; q=head; nhead=NULL; //创建新表头 while(q->next) { //删除最后一个链尾结点 p=q; while(p->next) { last=p; p=p->next; } value=p->val; last->next=NULL; free(p); //插入行结点 nq=nhead; while(nq) { last=nq; nq=nq->next; } if(nhead==NULL) { //创建表头 np=(struct ListNode*)malloc(sizeof(struct ListNode)); np->val=value; nhead=np; nhead->next=NULL; } else { //插入结点 np=(struct ListNode*)malloc(sizeof(struct ListNode)); np->val=value; np->next=NULL; last->next=np; } //q结点循环前始终指向链表头 q=head; } //最后一个结点及头结点head nq=nhead; while(nq) { last=nq; //使用nq=np总是报错WR nq=nq->next; } np=(struct ListNode*)malloc(sizeof(struct ListNode)); np->val=head->val; np->next=NULL; last->next=np; //nq->next=np会报错RE 因为nq此时为next及null,而nq->next更不知道在哪 return nhead; } (By:Eastmount 2015-9-14 晚上7点 http://blog.csdn.net/eastmount/)
题目概述: Given two strings s and t, write a function to determine if t is an anagram of s. For example, s = "anagram", t = "nagaram", return true. s = "rat", t = "car", return false.Note: You may assume the string contains only lowercase alphabets.解题方法: 该题意是比较两个字符串s和t,其中t是次序打乱的字符串,如果两个字符串相同则返回true,否则false。方法包括:(参考) 方法一 最简单的方法就是字符串s和t分别排序,在比较两个字符串是否相同。但是会报错TLE-Time Limit Exceeded 同样采用选择排序每次比较最小字符,不同则跳出循环返回false也TLE。bool isAnagram(char* s, char* t) { int ls,lt; //字符串长度 int i,j; char ch; if(s==NULL&&t==NULL) return true; ls=strlen(s); lt=strlen(t); if(ls!=lt) return false; //方法一 排序后判断字符串是否相等 for(i=0; i<ls; i++) { for(j=i+1; j<ls; j++) { if(s[i]>=s[j]) { ch=s[i]; s[i]=s[j]; s[j]=ch; } if(t[i]>=t[j]) { ch=t[i]; t[i]=t[j]; t[j]=ch; } } } if(strcmp(s,t)==0) return true; else return false; } 方法二 后来百度下发现如果采用Java代码,通过调用内部的sort排序则会AC,但个人不喜欢调用内部函数的方法。public class Solution { public boolean isAnagram(String s, String t) { char[] sArr = s.toCharArray(); char[] tArr = t.toCharArray(); Arrays.sort(sArr); Arrays.sort(tArr); return String.valueOf(sArr).equals(String.valueOf(tArr)); } } C++调用sort排序代码如下:class Solution { public: bool isAnagram(string s, string t) { sort(s.begin(), s.end()); sort(t.begin(), t.end()); return s == t; } }; 方法三 计算字符串字母个数,比较值都相同则true,否则返回false。 方法四 (强推) 通过一个长度为26的整形数组,对应英文中的26个字母a-z。从前向后循环字符串s和t,s中出现某一字母则在该字母在数组中对应的位置上加1,t中出现则减1。如果在s和t中所有字符都循环完毕后,整型数组中的所有元素都为0,则可认为s可由易位构词生成t。bool isAnagram(char* s, char* t) { int ls,lt; //字符串长度 int i; int num[26]={0}; if(s==NULL&&t==NULL) return true; ls=strlen(s); lt=strlen(t); if(ls!=lt) return false; //方法四 计算字母个数 s中出现+1,t中出现-1,整个数组26个数都为0时则表示相同 for(i=0; i<ls; i++) { num[s[i]-'a']++; num[t[i]-'a']--; } for(i=0; i<26; i++) { if(num[i]!=0) return false; } return true; } 而且最后的时间结果也比较优秀:C++调用sort代码-76ms; Java调用sort代码-288ms;C语言计算字母个数-0ms。(By:Eastmount 2015-9-14 清晨7点半 http://blog.csdn.net/eastmount/)
目录:1.Number of 1 Bits - 计算二进制1的个数 [与运算] 2.Contains Duplicate - 是否存在重复数字 [遍历]3.Reverse Integer - 翻转整数 [int边界问题]4.Excel Sheet Column Number - Excel字符串转整数 [简单]5.Power of Two & Happy Number - 计算各个位数字 [%10 /10] 一.Number of 1 Bits 题目概述:Write a function that takes an unsigned integer and returns the number of ’1' bits it has (also known as the Hamming weight).For example, the 32-bit integer ’11' has binary representation 00000000000000000000000000001011so the function should return 3. 解题方法: 三种方法包括: 1.依次和0x1进行&与运算,若结果为1则加1,表示1个数,再右移; 2.推荐的方法,n&(n-1),直到为0,次数为1的个数; 3.n取2模,依次判断个位是否为1,在n/2移位,常规方法。 其中uint32_t为32位无符号类型数据,参考地址Power of Two题目也可以通过return (n > 0) && (!(n & (n - 1)))一句话实现。Reverse Bits题目也可以<<移位实现。 我的代码: /* * uint32_t为32位无符号类型数据 思路:数字移位 */ int hammingWeight(uint32_t n) { //第一种方法 考查移位及与运算& int result=0, left=0; while(0 != n) { left = n & 0x1; result += left; n = n >> 1; } return result; //第二种方法 int re = 0; while(0 != n) { n = n&(n - 1); ++re; } return re; //第三种方法 求2模 int count = 0; while (n) { if (n % 2 == 1) { ++count; } n /= 2; } return count; } 二.Contains Duplicate 题目概述:Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct. 题目解析:题目是给定一个整数数组,判断该数组中是否存在重复的数字。简单AC的方法比较简单,两层循环判断;但是如果要求是O(n)的时间和O(1)的空间,怎样实现呢?腾讯的笔试题就考到了。又见重复判断II III题。 我的代码: bool containsDuplicate(int* nums, int numsSize) { //最傻的方法循环判断 int i,j; if(numsSize==0) return false; for(i=0;i<numsSize;i++) { for(j=i+1;j<numsSize;j++) { if(nums[i]==nums[j]) { return true; //表示存在重复的 } } } return false; } 推荐代码: 三.Reverse Integer 题目概述:Reverse digits of an integer.Example1: x = 123, return 321Example2: x = -123, return -321 解题思路:该题主要是考察整数的翻转问题,最简单的方法就是:通过"%10"计算个位数字和"/10"循环进行,直到整数为结果0;但是你需要注意的是: 1.负数的转换x=x*(-1) 2.整数越界,int型范围是(-2147483648~2147483647),4字节。当x=1534236469时,应该输出0而不是9646324351或溢出后的数 3.需要注意一个特殊的用例:x=-2147483648。此时x=x*(-1)=2147483648溢出,结果应是0。故此处需要把整数范围的判断指定出来讲解,没考虑整数溢出的代码如下: //翻转数字 显然采用前面做过的%10提取个位和/10方法 int reverse(int x) { int i,j; int num; //存储位数 int result; //结果 bool flag; //是否是负数 true负数 if( (x>=0&&x<10)||(x<0&&x>-10)) return x; if(x>0) flag=true; else { flag=false; x=x*(-1); } result=0; while(x!=0) { result=result*10+x%10; //结果 x=x/10; } if(flag==true) return result; else return result*(-1); } 正确代码:重点是类似于Java的Integer.MAX_VALUE 这种直接可用的数值型的最大值定义,C语言采用#include <limits.h>里面的INT_MIN和INT_MAX,而不是写一长串数字。/** * 翻转数字 刚做完翻转二叉树做该题还是感觉数字亲切点 * 显然采用前面做过的%10提取个位和/10方法 * 方法很简单 但是需要注意越界和x==-2147483648变成正数时溢出 */ int reverse(int x) { int i,j; int num; //存储位数 int result; //结果 bool flag; //是否是负数 true负数 if( (x>=0&&x<10)||(x<0&&x>-10)) return x; if(x==INT_MIN) //否则扭转溢出 INT_MIN=-2147483648 return 0; if(x>0) { flag=true; } else { flag=false; x=x*(-1); } result=0; while(x!=0) { if(x!=0&&result>INT_MAX/10) { //214748364 return 0; } result=result*10+x%10; //结果 x=x/10; //printf("%d\n",result); } if(flag==true) return result; else return result*(-1); } 四.Excel Sheet Column Number 题目概述:Related to question Excel Sheet Column TitleGiven a column title as appear in an Excel sheet, return its corresponding column number.For example: A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 题目解析:该类题目比较简单,主要考察字符串遍历和整数进制问题(26进制),自己一次AC。 int titleToNumber(char* s) { int result; int length; int i,j; if(s==NULL) return 0; length=strlen(s); result=0; //从右向左遍历 个位右 for(i=0; i<length; i++) { result=result*26+(s[i]-'A')+1; } return result; } 五.Power of Two & Happy Number 题目概述:判断数字是否是2的次数数判断数字是否是happy数,结果为1则返回trueExample: 19 is a happy number 1^2 + 9^2 = 82 8^2 + 2^2 = 68 6^2 + 8^2 = 100 1^2 + 0^2 + 0^2 = 1主要考察计算数字每个位数的方法,即n%10和n=n/10的方法。我的代码:Power of Two 强推位操作判断16=10000即高位为1其他为0,或通过一句话即可: return (n >0) && (!(n & (n -1)))http://www.bubuko.com/infodetail-953320.html bool isPowerOfTwo(int n) { int number; if(n<=0) return false; if(n==1) return true; //1=2^0 if(n%2!=0) return false; while(n>0) { if(n==1) { //最后一个数字是1 return true; } if(n%2!=0) { return false; } else { n=n/2; } } } Happy Numberbool isHappy(int n) { //重点:可能出现无限循环或数组越界情况 哪种情况不是happy数 int result; //结果直至0 int number; if(n<=0) return false; while(result!=1) { //计算result result = 0; while(n>0) { number = n%10; n = n/10; result = result + number*number; } if(result==1) { return true; } else if(result<10) { //输入2返回false return false; } else { n = result; //下一计算n为上次的结果 } } } PS:最后希望文章对您有所帮助,这都是自己A题的一些笔记和心得,同时真心建议你自己动手做做LeetCode。以前我也不信邪,现在信了~(By:Eastmount 2015-9-14 凌晨5点半 http://blog.csdn.net/eastmount/)
目录:1.Invert Binary Tree - 二叉树翻转 [递归]题目概述: Invert a binary tree. 4 / \ 2 7 / \ / \ 1 3 6 9 to 4 / \ 7 2 / \ / \ 9 6 3 1 Trivia: This problem was inspired by this original tweet by Max Howell: Google: 90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so fuck off. 题目分析: 题目背景是MaxHowell(他是苹果电脑最受欢迎的homebrew程序作者)去Google面试,面试官说:“虽然在Google有90%的工程师用你写的Homebrew,但是你居然不能再白板上写出翻转二叉树的代码,所以滚带吧”! 该题最初想法就是通过递归依次交换左右结点,但是想得太多,如“是否需要再建一颗树”、“是否需要引入队列或BFS”,最终没有AC。 我的代码: /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ //思路:二叉树递归 左右结点交换 在递归翻转左右子树 class Solution { public: TreeNode* invertTree(TreeNode* root) { TreeNode *left, *right; if(root==NULL) return root; left = root->left; right = root->right; root->left = invertTree(right); root->right = invertTree(left); return root; } }; 其他代码: 推荐阅读和采用二叉树非递归层次遍历算法实现如下。 你会翻转二叉树吗?--谈程序员的招聘 http://blog.csdn.net/sunao2002002/article/details/46482559 //第二种方法 通过队列层次遍历队列实现 class Solution { public: TreeNode* invertTree(TreeNode* root) { queue<TreeNode*> q; if(root==NULL) return root; q.push(root); int size=q.size(); while(size>0) { TreeNode *p=q.front(); q.pop(); //交换左右结点 TreeNode *LNode=p->left; TreeNode *RNode=p->right; p->left=RNode; p->right=LNode; //进入队列 if(p->left) { q.push(p->left); } if(p->right) { q.push(p->right); } size=q.size(); } return root; } }; 其他题目: (By:Eastmount 2015-9-12 凌晨5点半 http://blog.csdn.net/eastmount/)
目录:1.Binary Tree Level Order Traversal - 二叉树层次遍历 BFS 2.Binary Tree Level Order Traversal II - 二叉树层次遍历从低往高输出 BFS 3.Maximum Depth of Binary Tree - 求二叉树的深度 DFS4.Balanced Binary Tree - 判断平衡二叉树 DFS5.Path Sum - 二叉树路径求和判断DFS 题目概述:Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).For example:Given binary tree {3,9,20,#,#,15,7}, 3 / \ 9 20 / \ 15 7 return its level order traversal as: [ [3], [9,20], [15,7] ] Here's an example: 1 / \ 2 3 / 4 \ 5 The above binary tree is serialized as "{1,2,3,#,#,4,#,#,5}". 题目分析: 本题考查的就是二叉树的层次遍历,需要注意的是二叉树用数组的表示方法,二叉树的每层是从左到右存入数组的。方法包括: 1.层次遍历。二维数组存储数字和深度,输出二维数组即可,过于复杂。 2.通过队列BFS广度优先搜索。 3.通过DFS深度优先搜索实现。 我的代码: /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: //二叉树层次遍历 通过队列BFS广度优先搜索 vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; queue<TreeNode*>q; vector<int> level; //每层结果 int size,i; TreeNode* p; if(root==NULL) return result; q.push(root); //入队 while(!q.empty()) { //队列中有几个元素就依次遍历每个元素的左右结点 level.clear(); size=q.size(); for(i=0; i<size; i++) { p=q.front(); //队首元素值赋给p q.pop(); //出队 level.push_back(p->val); if(p->left) { //依次压入左右结点元素 q.push(p->left); } if(p->right) { q.push(p->right); } } result.push_back(level); //添加每层数据 } return result; } }; 代码详解: 该题目你如果采用C语言二维数组过于复杂,故采用C++的容器vector实现。同时BFS广度优先搜索采用队列queue实现,常见方法如下(参考地址): 1.栈操作 #include<stack> 头文件 stack<int> s 定义栈 s.empty() 如果栈为空返回true,否则返回false s.size() 返回栈中元素的个数 s.pop() 删除栈顶元素但不返回其值 s.top() 返回栈顶的元素,但不删除该元素 s.push() 在栈顶压入新元素 2.队列操作#include<queue> 头文件 queue<int> q 定义队列 q.empty() 如果队列为空返回true,否则返回false q.size() 返回队列中元素的个数 q.pop() 删除队列首元素但不返回其值 q.front() 返回队首元素的值,但不删除该元素 q.push() 在队尾压入新元素 q.back() 返回队列尾元素的值,但不删除该元素 3.二叉树层次遍历如何使用队列 由于二叉树是从左至右进行输入,故层次遍历通过队列存储每层的结点,它存储的顺序也是前一个结点的左孩子结点、右孩子结点,依次顺序进出队列。 DFS代码参考地址:LeetCode Binary Tree Level Order Traversal 其他题目:Binary Tree Level Order Traversal II 层次遍历从低往root结点输出,如 Given binary tree {3,9,20,#,#,15,7}, 3 / \ 9 20 / \ 15 7 return its level order traversal as: [ [15,7], [9,20], [3] ] 最简单方法通过层次遍历BFS调用队列后逆序倒置vector容器即可。/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: vector<vector<int>> levelOrderBottom(TreeNode* root) { vector<vector<int>> result; vector<int> level; queue<TreeNode*> q; if(root==NULL) return result; q.push(root); while(!q.empty()) { //层次遍历 level.clear(); int size=q.size(); for(int i=0; i<size; i++) { //注意:不能i<q.size() 当入队时它会变换 TreeNode* p=q.front(); q.pop(); level.push_back(p->val); if(p->left) q.push(p->left); if(p->right) q.push(p->right); } //每层结果存入容器 result.push_back(level); } /* * 逆序输出 倒置容器调用函数 * reverse(result.begin(),result.end()); * return result; */ vector<vector<int>>::iterator iter; //迭代器 vector<vector<int>> res; for(iter=result.end()-1; iter!=result.begin()-1; iter--) { level.clear(); for(int i=0; i<(*iter).size(); i++) //复制每层内容 { level.push_back((*iter)[i]); } res.push_back(level); } return res; } }; PS:如果是每层的也要逆序的话,就把left 和right 入队的顺序调换一下。另一种遍历方法参考:http://www.cnblogs.com/ganganloveu/p/3843470.html Maximum Depth of Binary Tree - 求二叉树的深度 常见方法通过BFS层次遍历计算二叉树层数及深度或通过DFS计算二叉树从root到leaf结点最长路径及深度,在采用BSF代码中可通过前面代码进行修改,但错误: [0,2,4,1,null,3,-1,5,1,null,6,null,8] output=5 Excepted=4 故采用DFS进行深度递归搜索。代码如下:int maxDepth(struct TreeNode* root) { if(root == NULL) return 0; int left = maxDepth(root->left); int right = maxDepth(root->right); return (left >= right ? left : right) + 1; } BFS代码参考:http://blog.csdn.net/sunbaigui/article/details/8980887 Balanced Binary Tree - 判断平衡二叉树 平衡二叉树是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。参考前面的计算深度方法完成。 int DFS(struct TreeNode* p) { if(p==NULL) return 0; int left=DFS(p->left); int right=DFS(p->right); return (left>=right?left:right)+1; } //递归判断左右子树结点是否符合平衡二叉树 bool isBalancedNode(struct TreeNode* L,struct TreeNode* R) { if(!L&&!R) return true; //自定义DFS函数计算结点的深度 int left=DFS(L); int right=DFS(R); //平衡二叉树左右结点深度相差0或1 if(abs(left-right)>1) return false; else if(L&&R) //必须存在 否则会报错RE [1,2]时R->left不存在 return isBalancedNode(L->left,L->right) && isBalancedNode(R->left,R->right); } bool isBalanced(struct TreeNode* root) { if(root==NULL) return true; if(root&&!root->left&&!root->right) return true; else return isBalancedNode(root->left,root->right); } 另一种方法参考地址,也可通过后序遍历实现。/** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool checkBalance(TreeNode *node, int &dep) { if (node == NULL) { dep = 0; return true; } int leftDep, rightDep; bool leftBalance = checkBalance(node->left, leftDep); bool rightBalance = checkBalance(node->right, rightDep); dep = max(leftDep, rightDep)+1; return leftBalance && rightBalance && (abs(rightDep - leftDep) <= 1); } bool isBalanced(TreeNode *root) { int dep; return checkBalance(root, dep); } }; Path Sum - 二叉树路径求和判断 Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.For example: Given the below binary tree and sum = 22, 5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1 return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22. 该题主要考察DFS或BFS计算root到leaf结点路径求和是否存在一条与sum相等的path。我采用DFS结合计算二叉树深度完成,最初打算自定义isNode(*root,num)函数判断,后来直接通过判断每个结点是否是leaf且值为sum-前面结点。代码如下:/** * Definition for a binary tree node. * struct TreeNode { * int val; * struct TreeNode *left; * struct TreeNode *right; * }; */ //思路:通过DFS计算root-to-leaf的结果 bool hasPathSum(struct TreeNode* root, int sum) { if(root==NULL) return false; else if(root&&!root->left&&!root->right&&root->val==sum) //仅root结点 return true; else if(root&&!root->left&&!root->right&&root->val!=sum) return false; else //包括子结点 return hasPathSum(root->left,(sum - root->val)) || hasPathSum(root->right,(sum - root->val)); } (By:Eastmount 2015-9-11 凌晨3点半 http://blog.csdn.net/eastmount/)
题目概述:Given a sorted linked list, delete all duplicates such that each element appear only once.For example,Given 1->1->2, return 1->2. Given 1->1->2->3->3, return 1->2->3.题目解析:这是一道非常简单的链表题目,题意是删除单链表(已排序)中的重复数字,只需一次判断前后两个结点数字是否相等即可。需要注意几点: 1.该链表中头结点就开始存储数字head->val存在; 2.初始判断if(head==NULL || head->next==NULL),防止出现'[]'或'[1]'; 3.判断过程中使用q->next->val==q->val和释放free临时结点p; 4.若使用中间变量number=q->val判断时,当else中q指向下一个结点为空时,该句不存在number=q->val会报错RE,如'[1,1]'。故不建议使用临时变量。总之,一道非常基础的链表题目,不需要过于复杂化代码。 我的代码: /** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* deleteDuplicates(struct ListNode* head) { struct ListNode *p,*q; q=head; if(head==NULL || head->next==NULL) //防止[]和[1] return head; while(q) { if( q->next!=NULL && q->next->val==q->val ) { //删除操作 最后free p=q->next; q->next=p->next; free(p); } else { q=q->next; } } return head; } 其他类型链表题目:(By:Eastmount 2015-9-10 凌晨3点半 http://blog.csdn.net/eastmount/)
题目概述:Given a non-negative number represented as an array of digits, plus one to the number. The digits are stored such that the most significant digit is at the head of the list.题目解析:给你一个int型数组存储一个非负整数,对整数加1后输出一个int型数组。注意几点: 1.可能存在进位操作,增加一位,如999+1=1000; 2.数组存储如234=[2, 3, 4],它进行加1操作时从数组的高位(4)到低位(2); 3.输出时也需要转置[0, 0, 0, 1]转成1000; 4.C语言代码*returnSize是一维数组,注意赋值否则提示“超时异常”。我的代码:/** * Return an array of size *returnSize. * Note: The returned array must be malloced, assume caller calls free(). * 899+1=900 存储时digits[3]=[8,9,9] 从高位到地位 result[3]=[0,0,9]需要转置 * digitsSize数组长度 *returnSize为返回数组长度 */ int* plusOne(int* digits, int digitsSize, int* returnSize) { //初始时加1操作 后为进位数字0或1 int add=1; int i,j=0; int temp; //申请空间 初始化操作 int *result=(int*)malloc(sizeof(int)*(digitsSize+1)); memset(result, 0 , sizeof(int)*(digitsSize + 1)); for(i=digitsSize-1; i>=0; i--) { result[j]=(digits[i]+add)%10; //个位数字 add=(digits[i]+add)/10; //进位操作 j++; } //最后如果add==1表示位数加1 如99+1=100 if(add==1) { result[digitsSize]=1; *returnSize=digitsSize+1; //注意它是一维数组 //输出数组倒置 for(i=0,j=digitsSize;i<j;i++,j--) { temp=result[i]; result[i]=result[j]; result[j]=temp; } } else { *returnSize=digitsSize; //输出数组倒置 for(i=0,j=digitsSize-1;i<j;i++,j--) { temp=result[i]; result[i]=result[j]; result[j]=temp; } } return result; } 推荐代码: C语言代码 参考:http://www.tonzoc.info/?p=688/** * Return an array of size *returnSize. * Note: The returned array must be malloced, assume caller calls free(). */ void reverse(int* digits, int start, int end) { int temp; for (int i = start; i <= (start + end) >> 1; ++i) { temp = digits[i]; digits[i] = digits[end + start - i]; digits[end + start - i] = temp; } } int* plusOne(int* digits, int digitsSize, int* returnSize) { int num = 1; int* result = (int*)malloc(sizeof(int) * (digitsSize + 1)); memset(result, 0, sizeof(int) * (digitsSize + 1)); for (int i = digitsSize - 1; i >= 0; --i) { result[digitsSize - 1 - i] = (digits[i] + num) % 10; num = (digits[i] + num) / 10; } if (num) { *returnSize = digitsSize + 1; result[digitsSize] = num; reverse(result, 0, digitsSize); } else { *returnSize = digitsSize; reverse(result, 0, digitsSize - 1); } return result; } C++代码vector<int> plusOne(vector<int> &digits) { int carry=1, sum=0; vector<int> result(digits.size(),0); for(int i=digits.size()-1;i>=0;i--){ sum = carry+digits[i]; carry = sum/10; result[i] = sum%10; } if(carry>0){ //进位 result.insert(result.begin(),carry); } return result; } 同类题目: 二进制字符串加法 https://leetcode.com/problems/add-binary/PS:需要注意转换方法 ((a[i]-'0')+(b[j]-'0')+add)%2+'0'当前结果和进位数字add=((a[i]-'0')+(b[j]-'0')+add)/2;同时需要注意字符串倒置的方法和对齐判断即可。 (By:Eastmount 2015-9-9 凌晨2点 http://blog.csdn.net/eastmount/)
题目概述:You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad. Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad. You are given an API boolisBadVersion(version) which will return whetherversion is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.题目解析:数组[1,2..n]中存在一个bad版本时,后面的版本都是bad,通过调用函数isBadVersion可以判断是否是bad版本。例如:[1,2,3]中2是bad版本,则调用isBadVersion(2)=true、isBadVersion(1)=false、isBadVersion(3)=true,结果返回2第一个导致bad的版本。解决方法:二分查找需注意middle=left+(right-left)/2、二分查找的下标移动和返回值left。 我的代码:// Forward declaration of isBadVersion API. bool isBadVersion(int version); /* * 二分查找 关键步骤: * 1.middle定位 * 2.大于middle查找右部分 left=middle+1 * 3.小于middle查找左部分 right=middle-1 */ int firstBadVersion(int n) { int middle; int left; int right; left=1; right=n; while(left<=right) { middle = left+(right-left)/2; //重点&能防止越界 例1+(5-1)/2=3 if(isBadVersion(middle)==true) { right = middle-1; } else { left = middle+1; } } return left; } 其他题目:(By:Eastmount 2015-9-9 凌晨2点 http://blog.csdn.net/eastmount/)
目录:1.Binary Tree Paths - 求二叉树路径 2.Same Tree - 判断二叉树相等 3.Symmetric Tree - 判断二叉树对称镜像 Binary Tree Paths 题目概述:Given a binary tree, return all root-to-leaf paths. For example, given the following binary tree: 1 / \ 2 3 \ 5 All root-to-leaf paths are: ["1->2->5", "1->3"] 题目解析:本题主要考察二叉树遍历操作,输出二叉树的所有路径,通常采用递归方法能很好的解决。但是如果采用C语言编写,返回二维字符串数组如何添加二叉树路径是个难点?char** binaryTreePaths(struct TreeNode* root, int* returnSize) {}最终采用C++完成,当遍历至叶子节点时,通过容器push_back添加一条路径。 我的代码:/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: //创建空容器 对象类型为string类 vector<string> result; void getPaths(TreeNode* node,string path) { if(node->left==NULL && node->right==NULL) { //左右子树为空 路径寻找完成 增加至数组中 result.push_back(path); } if(node->left!=NULL) { //递归遍历左子树 当前路径添加左孩子结点 getPaths(node->left,path+"->"+to_string(node->left->val)); } if(node->right!=NULL) { //递归遍历右子树 getPaths(node->right,path+"->"+to_string(node->right->val)); } } //获取二叉树路径 vector<string> binaryTreePaths(TreeNode* root) { if(root==NULL) return result; getPaths(root, to_string(root->val)); //to_string整数转换为字符串 return result; } }; 推荐代码:Java代码 地址:http://segmentfault.com/a/1190000003465753public class Solution { List<String> res = new ArrayList<String>(); public List<String> binaryTreePaths(TreeNode root) { if(root != null) findPaths(root,String.valueOf(root.val)); return res; } private void findPaths(TreeNode n, String path){ if(n.left == null && n.right == null) res.add(path); if(n.left != null) findPaths(n.left, path+"->"+n.left.val); if(n.right != null) findPaths(n.right, path+"->"+n.right.val); } } Same Tree 判断两颗二叉树是否相等,非递归方法通过isSameNode依次遍历结点/** * Definition for a binary tree node. * struct TreeNode { * int val; * struct TreeNode *left; * struct TreeNode *right; * }; */ //递归方法 bool isSameTree(struct TreeNode* p, struct TreeNode* q) { if(p==NULL&&q==NULL) return true; else if( (p!=NULL&&q==NULL) || (p==NULL&&q!=NULL) ) return false; else { if(p->val != q->val) return false; else return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); } } Symmetric Tree 题目概述:Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).For example, this binary tree is symmetric: 1 / \ 2 2 / \ / \ 3 4 4 3 But the following is not: 1 / \ 2 2 \ \ 3 3 Note: Bonus points if you could solve it both recursively and iteratively. 题目解析:判断二叉树是否为镜像对称二叉树,当时错误理解为判断完全二叉树。解题思路是通过比较左右结点,左结点->left和右结点->right比较、左结点->right和右结点->left比较。 非递归算法可以采用层次遍历,每次比较同一层的数是否镜像即可。我的代码:/** * Definition for a binary tree node. * struct TreeNode { * int val; * struct TreeNode *left; * struct TreeNode *right; * }; */ //比较左右结点 bool isSameNode(struct TreeNode* L, struct TreeNode* R) { if(L==NULL&&R==NULL) { return true; } else if((L!=NULL&&R==NULL) || (L==NULL&&R!=NULL)) { //其中一个为空 return false; } else if(L->val!=R->val) { return false; } else { return isSameNode(L->left,R->right) && isSameNode(L->right,R->left); } } //判断二叉树是否为镜像对称 bool isSymmetric(struct TreeNode* root) { if(!root) return true; else { return isSameNode(root->left,root->right); } } 非递归代码: 来源地址:http://blog.csdn.net/lc_910927/article/details/36180075class Solution { public: bool isSymmetric (TreeNode* root) { if (!root) return true; stack<TreeNode*> s; s.push(root->left); s.push(root->right); while (!s.empty ()) { auto p = s.top (); s.pop(); auto q = s.top (); s.pop(); if (!p && !q) continue; if (!p || !q) return false; if (p->val != q->val) return false; s.push(p->left); s.push(q->right); s.push(p->right); s.push(q->left); } return true; } }; PS:二叉树是面试中经常考察的题目,包括建立二叉树、遍历二叉树、二叉树交换、二叉树求和等。希望文章对你有所帮助,同时Java、C#、C++、C学杂了容易混乱,再次验证了学精的重要性。 (By:Eastmount 2015-9-9 凌晨1点 http://blog.csdn.net/eastmount/)
题目概述:Given a non-negative integer num, repeatedly add all its digits until the result has only one digit.For example:Given num = 38, the process is like: 3 + 8 = 11, 1 + 1 = 2. Since 2 has only one digit, return it.Follow up:Could you do it without any loop/recursion in O(1) runtime? 题目解析: 主要考察整数各个位数求和,当且仅当和小于10时输出。可能会走入整数转换为字符串的误区,直接使用取余(%)个位数相加即可,需要注意: 1.非负整数判断 2.输入0时输出0,输入10时输出1 3.至于O(1)时间复杂度考察数学公式:1 + (num-1) % 9 4.整数转换字符串可以类似思想:循环个位数转换reslut=num%10,num=num/10我的代码: int addDigits(int num) { int result; //负数跳出 if(num<=0) return 0; if(num<10) return num; result=num; while(result>=10) { num=result; result=0; while(num>=10) { //计算数字和 result+=num%10; //个位 num=num/10; } result+=num; } return result; } 推荐代码:public class Solution { public int addDigits(int num) { return 1 + (num-1) % 9; } } 题目笔记: Roman to Integer 罗马数字转换考察switch和数字各个位数转换int getNumToInt(char ch) { //此题需要注意switch写法和罗马数字计算 switch(ch) { case 'I': return 1; case 'V': return 5; case 'X': return 10; case 'L': return 50; case 'C': return 100; case 'D': return 500; case 'M': return 1000; default: return 0; } return 0; } int romanToInt(char* s) { /* 1~9: {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"} 10~90: {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"} 100~900: {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"} 1000~3000: {"M", "MM", "MMM"} 如果当前处理的字符对应的值和上一个字符一样,那么临时变量加上这个字符。比如III = 3 如果当前比前一个大,说明这一段的值应该是当前这个值减去前面记录下的临时变量中的值。比如IIV = 5 – 2 如果当前比前一个小,那么就可以先将临时变量的值加到结果中,然后开始下一段记录。比如VI = 5 + 1 DCXXI=500+100+10+10+1=621 */ int length; int i,j; int result; //结果 int last; //前一个值 int curr; //当前值 int num; //临时变量 length = strlen(s); last = getNumToInt(s[0]); num = last; for(i=1; i<length; i++) { curr = getNumToInt(s[i]); if(curr == last) { num += curr; } else if(curr < last) { result += num; num = curr; } else if(curr > last) { num = curr - num; } last = curr; } result += num; return result; } PS:我代码的缺点包括:我喜欢过于复杂的注释,而直观的代码好于注释;代码中的临时变量转换过多,需要更精简;字符串数组操作习惯计算长度后s[i],而不是使用*s。(By:Eastmount 2015-9-8 下午6点半 http://blog.csdn.net/eastmount/)
题目概述:Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the length of last word in the string. If the last word does not exist, return 0. Note: A word is defined as a character sequence consists of non-space characters only. For example, Given s = "Hello World",return 5. 题目解析: 在字符串含空格中计算最后一个单词的长度,主要考察字符串操作。最初我采用寻找空格找到最后一个单词的起始位置begin,计算该单词的长度end-begin即可。但是会遇到各种错误,如: 1.全空格字符串 如' ' 返回0 2.最后一个单词后面存在空格 如'day ' 返回3 3.单词前面存在多个空格 如' day' 返回3 4.复杂的情况 如' ab day ' 返回3 最后修改成判断当前字符不是空格时计数,有效避免全空格等用例。(作者AC的第一题) 我的代码: int lengthOfLastWord(char* s) { int length; //数组长度 int num=0; //返回最后一个单词的长度 int i,j; length = strlen(s); for(i=0; i<length; i++) { //建议当不是空格时计数 避免全空格 if(s[i]!=' ') { num=0; for(j=i; j<length && s[j]!=' '; j++) { //计算单词长度 始终记录最后一个单词 num++; } i=j; //防止出现计算单词'word'后再计算'ord'覆盖前一个长度 if(j>=length) break; //最后一个单词时直接跳出循环 } } return num; } 推荐代码: class Solution { public: int lengthOfLastWord(const char *s) { // Start typing your C/C++ solution below // DO NOT write int main() function if(s == NULL) return 0; int ans = 0; while(*s != '\0') { if(*s != ' ') { int curLen = 0; while(*s != '\0' && *s != ' ') s++, curLen++; ans = curLen; } else s++; } return ans; } }; 心得感受: 最近找工作发现很多基础的东西都忘记了,同时看《编程之美》、《剑指offer》又心不在焉,怎么办呢?只好找回自己最初的状态,督促自己A题来捡起遗忘的基础知识。LeetCode就是一个监督的平台吧!网上关于它的代码非常之多,我只想记录自己一些A题心得和当前的状态。作者真心想找到一份工作,开始新的生活,享受编程的乐趣了。 虽然我已经想好了不论去到什么公司都认认真真地学习干三五年,但在这之前当下这种找工作的随意心态还是需要改正,需要付出和认真对待。当然并不是写博客的人就多么的厉害(很多牛人只专注于编码),并不是学习好的就多么的有成就,并不是找到好工作后就能抓住幸福,但是脚踏实地的做事和享受编程分享的乐趣还是永存的。 正如钱钟书在《围城》里说的一样:“城外的人拼命往里挤,城里的人拼命往外跑”。其实不管是城里人还是城外人,所做的一切,无非就是为了幸福。然后幸福在哪里?所以且行且珍惜,享受生活和工作,换种心态,程序员的生活同样美好! (By:Eastmount 2015-9-8 晚上5点半 http://blog.csdn.net/eastmount/)