自己动手做大数据系统

第1章 为什么要自己动手做大数据系统(略)
第2章 项目背景及准备(略)

项目总体框架图,从数据获取->数据存储->数据处理->数据分析与展示
第3章 大数据环境搭建和配置
3.1 各组件功能说明
3.1.1 各种数据源的采集工具
3.1.2 企业大数据存储工具
3.1.3 企业大数据系统的数据仓库工具
3.1.4 企业大数据系统的分析计算工具
3.1.5 企业大数据系统的数据库工具
3.2 大数据系统各组件安装部署配置
3.2.1 安装的前期准备工作
3.2.2 Hadoop基础环境安装及配置
3.2.3 Hive安装及配置
3.2.4 Sqoop安装及配置
3.2.5 Spark安装及配置
3.2.6 Zookeeper安装及配置
3.2.7 HBase安装及配置
3.3 自动化安装及部署说明
3.3.1 自动化安装及部署整体架构设计
3.3.2 大数据系统自动化部署逻辑调用关系
3.4 本章小结
第4章 大数据的获取
4.1 使用爬虫获取互联网数据
4.2 Python和Scrapy 框架的安装
4.3 抓取和解析招聘职位信息
4.4 职位信息的落地
4.5 两个爬虫配合工作
4.6 让爬虫的架构设计更加合理
4.7 获取数据的其他方式
4.8 使用Sqoop同步论坛中帖子数据
4.9 本章小结
第5章 大数据的处理
5.1 Hive是什么
5.2 为什么使用Hive做数据仓库建模
5.3 飞谷项目中Hive建模步骤
5.3.1 逻辑模型的创建
5.3.2 物理模型的创建
5.3.3 将爬虫数据导入stg_job表
5.4 使用Hive进行数据清洗转换
5.5 数据清洗转换的必要性
5.6 使用HiveQL清洗数据、提取维度信息
5.6.1 使用HQL清洗数据
5.6.2 提取维度信息
5.7 定义Hive UDF封装处理逻辑
5.7.1 Hive UDF的开发、部署和调用
5.7.2 Python版本的UDF
5.8 使用左外连接构造聚合表rpt_job
5.9 让数据处理自动调度
5.9.1 HQL的几种执行方式
5.9.2 Hive Thrift服务
5.9.3 使用JDBC连接Hive
5.9.4 Python调用HiveServer服务
5.9.5 用crontab实现的任务调度
5.10 本章小结
第6章 大数据的存储
6.1 NoSQL及HBase简介
6.2 HBase中的主要概念
6.3 HBase客户端及JavaAPI
6.4 Hive数据导入HBase的两种方案
6.4.1 利用既有的JAR包实现整合
6.4.2 手动编写MapReduce程序
6.5 使用Java API查询HBase中的职位信息
6.5.1 为什么是HBase而非Hive
6.5.2 多条件组合查询HBase中的职位信息
6.6 如何显示职位表中的某条具体信息
6.7 本章小结
第7章 大数据的展示
7.1 概述
7.2 数据分析的一般步骤
7.3 用R来做数据分析展示
7.3.1 在Ubuntu上安装R
7.3.2 R的基本使用方式
7.4 用Hive充当R的数据来源
7.4.1 RHive组件
7.4.2 把R图表整合到Web页面中
7.5 本章小结
第8章 大数据的分析挖掘
8.1 基于Spark的数据挖掘技术
8.2 Spark和Hadoop的关系
8.3 在Ubuntu上安装Spark集群
8.3.1 JDK和Hadoop的安装
8.3.2 安装Scala
8.3.3 安装Spark
8.4 Spark的运行方式
8.5 使用Spark替代Hadoop Yarn引擎
8.5.1 使用spark-sql查看Hive表
8.5.2 在beeline客户端使用Spark引擎
8.5.3 在Java代码中引用Spark的ThriftServer
8.6 对招聘公司名称做全文检索
8.6.1 从HDFS数据源构造JavaRDD
8.6.2 使用Spark SQL操作RDD
8.6.3 把RDD运行结果展现在前端
8.7 如何把Spark用得更好
8.8 SparkR组件的使用
8.8.1 SparkR的安装及启动
8.8.2 运行自带的Sample例子
8.8.3 利用SparkR生成职位统计饼图
8.9 本章小结

Spark入门

第5章 Spark入门
Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,2014年至2015年,Spark经历了高速发展,Databricks 2015 Spark调查报告显示:2014年9月至2015年9月,已经有超过600个Spark源码贡献者,而在此之前的12个月人数只有315,Spark超越Hadoop,无可争议地成为大数据领域内最活跃的开源项目。除此之外,已经有超过200个公司为Spark奉献过源代码,使Spark社区成为迄今为止开发人员参与最多的社区。
Spark以其先进的设计理念,迅速成为社区的热门项目,围绕着Spark推出了Spark SQL、Spark Streaming、MLLib和GraphX等组件,也就是BDAS(伯克利数据分析栈),这些组件逐渐形成大数据处理一站式解决平台。
Spark使用Scala语言进行实现,它是一种面向对象、函数式编程语言,能够像操作本地集合对象一样轻松地操作分布式数据集(Scala 提供一个称为 Actor 的并行模型,其中Actor通过它的收件箱来发送和接收非同步信息而不是共享数据,该方式被称为:Shared Nothing 模型)。在Spark官网上介绍,它具有运行速度快、易用性好、通用性强和随处运行等特点。
 运行速度快
Spark拥有DAG执行引擎,支持在内存中对数据进行迭代计算。官方提供的数据表明,如果数据由磁盘读取,速度是Hadoop MapReduce的10倍以上,如果数据从内存中读取,速度可以高达100多倍。

 易用性好
Spark不仅支持Scala编写应用程序,而且支持Java和Python等语言进行编写,特别是Scala是一种高效、可拓展的语言,能够用简洁的代码处理较为复杂的处理工作。
 通用性强
Spark生态圈包含了Spark Core、Spark SQL、Spark Streaming、MLLib和GraphX等组件,这些组件分别处理Spark Core提供内存计算框架、SparkStreaming的实时处理应用、Spark SQL的即席查询、MLlib或MLbase的机器学习和GraphX的图处理,它们无缝集成于Spark平台。
 随处运行
Spark具有很强的适应性,能够读取HDFS、HBase、S3和Techyon等的数据,能够以Mesos、YARN和自身携带的Standalone作为资源管理器调度job,来完成Spark应用程序的计算。

5.1 Spark架构及原理
进入交换式编程界面,在之前的Spark版本中,Spark shell会自动创建一个SparkContext对象sc。2.0中Spark shell则会自动创建一个SparkSession对象(spark),在输入spark时就会发现它已经存在了。下图说明了SparkContext在Spark中的主要功能。

从图中可以看到SparkContext起到的是一个中介的作用,通过它来使用Spark其他的功能。每一个JVM都有一个对应的SparkContext,Driver program通过SparkContext连接到集群管理器来实现对集群中任务的控制。Spark配置参数的设置以及对SQLContext、HiveContext和StreamingContext的控制也要通过SparkContext。不过在Spark2.0中上述的一切功能都是通过SparkSession来完成的,同时SparkSession也简化了DataFrame/Dataset API的使用和对数据的操作。SparkSession成为Spark的一个全新的切入点。

5.2 Spark SQL简介
Spark Sql 的前身是shark,最初是用来查询Hive,Hive是为熟悉数据库,但不熟悉MapReduce的开发人员提供的工具,Hive提供可以对数据进行提取转化和加载(简称ETL)功能,通过Hive工具可以查询分析存储在Hadoop上的大规模数据。Hive定义了简单的查询语言HQL(类SQL),可以把SQL操作转成MapReduce任务。
MapReduce的计算过程消耗大量的IO,降低了运行效率,为了提高查询Hadoop上的数据的效率,出现了很多工具,如Impla 和shark等,其中Shark是spark的生态组件之一,它复用了Hive的SQL解析等组件,修改了内存管理,物理计划、执行模块,使它能够运行在Spark的计算引擎上,使用SQL的查询速度有了10-100倍的提升。
随着shark的发展,shark对Hive的依赖限制了其发展,包括语法解析器和查询优化器。Spark团队汲取了shark的优点重新设计了Spark SQL,使之在数据兼容、性能优化、组件扩展等方面得到极大的提升。
 数据兼容:不仅兼容Hive,还可以从RDD、parquet文件、Json文件获取数据、支持从RDBMS获取数据。
 性能优化:采用内存列式存储、自定义序列化器等方式提升性能。
 组件扩展:SQL的语法解析器、分析器、优化器都可以重新定义和扩展。
Spark SQL是Spark提供的针对结构化数据处理的模块。不同于基本的Spark RDD API,SparkSQL提供的接口提供了更多的关于数据和计算执行的信息。其数据源可以是文件、Hive表、其他数据库等,我们可以通过command-line或者JDBC/ODBC与SQL接口进行交互。

5.3 Spark SQL操作MySQL

//启动spark客户端
spark-shell --master spark://master:7077 --driver-memory 1G
//连接mysql数据库
scala>val jdbcDF = spark.read.format("jdbc").options(Map("url" ->"jdbc:mysql://192.168.1.106:3306/testdb", "driver" ->"com.mysql.jdbc.Driver", "dbtable" ->"testdb.stud_info", "user" ->"feigu", "password" ->"feigu")).load()
//创建一个视图
scala>jdbcDF.createOrReplaceTempView("stud_info")
scala> val sqlDF=sql("select * from stud_info limit 5")
sqlDF: org.apache.spark.sql.DataFrame = [stud_code: string, stud_name: string ... 8 more fields]

scala> sqlDF.show()
+----------+---------+--------+------+--------+------------+------------+-----+
| stud_code|stud_name|stud_sex| birthday| log_date|orig_addr|lev_date|college_code|college_name|state|
+----------+---------+--------+----------+---+------------+------------+-----+
|2015101000| 王进| M|1997-08-01|2014-09-01| 苏州| null| 10| 理学院| 1
|2015101001| 刘海| M|1997-09-29|2014-09-01| 上海| null| 10| 理学院| 1
|2015101002| 张飞| M|1996-10-21|2014-09-02| 济南| null| 10| 理学院| 1
|2015101003| 刘婷| F|1998-01-10|2014-09-01| 北京| null| 10| 理学院| 1
|2015101004| 卢家| M|1997-08-01|2014-09-01| 南京| null| 10| 理学院| 1

5.4 Spark SQL操作Hive
Spark SQL与Hive的交换,当在Hive上工作时,必须实例化SparkSession对Hive的支持,包括对持久化Hive元存储的连通性,对Hive序列化反序列化,Hive用户自定义函数的支持。当没有在hive-site.xml配置是,context会自动在当前目录创建metastore_db并且创建一个被spark.sql.warehouse.dir配置的目录,默认在spark应用启动的当前目录的spark-warehouse。注意从Spark2.0.0开始hive-site.xml中的hive.metastore.warehouse.dir参数被弃用。作为替代,使用spark.sql.warehouse.dir来指定仓库中数据库的位置。你可能需要授权写权限给启动spark应用的用户。

spark-shell --master spark://master:7077 --driver-memory 1G --total-executor-cores 2
import org.apache.spark.sql.Row
import org.apache.spark.sql.SparkSession

case class Record(key: Int, value: String)

// warehouseLocation points to the default location for managed databases and tables
val warehouseLocation = "spark-warehouse"

val spark = SparkSession
.builder()
.appName("Spark Hive Example")
.config("spark.sql.warehouse.dir", warehouseLocation)
.enableHiveSupport()
.getOrCreate()

import spark.implicits._
import spark.sql

sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
sql("LOAD DATA LOCAL INPATH '/home/hadoop/bigdata/spark/examples/src/main/resources/kv1.txt' INTO TABLE src")

// Queries are expressed in HiveQL
sql("SELECT * FROM src limit 3").show()

// +---+-------+
// |key| value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...

// Aggregation queries are also supported.
sql("SELECT COUNT(*) FROM src").show()
// +--------+
// |count(1)|
// +--------+
// | 500 |
// +--------+

// The results of SQL queries are themselves DataFrames and support all normal functions.
val sqlDF = sql("SELECT key, value FROM src WHERE key < 10 ORDER BY key") // The items in DaraFrames are of type Row, which allows you to access each column by ordinal. val stringsDS = sqlDF.map { case Row(key: Int, value: String) => s"Key: $key, Value: $value"
}
stringsDS.show()
// +--------------------+
// | value|
// +--------------------+
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// ...

// You can also use DataFrames to create temporary views within a SparkSession.
val recordsDF = spark.createDataFrame((1 to 100).map(i => Record(i, s"val_$i")))
recordsDF.createOrReplaceTempView("records")

// Queries can then join DataFrame data with data stored in Hive.
sql("SELECT * FROM records r JOIN src s ON r.key = s.key").show()
// +---+------+---+------+
// |key| value|key| value|
// +---+------+---+------+
// | 2| val_2| 2| val_2|
// | 4| val_4| 4| val_4|
// | 5| val_5| 5| val_5|

5.5 pyspark操作hive
以下代码操作hive中表:fund.rpt_fund的数据,fund是数据库名称,rpt_fund是表名。

from pyspark.sql import SparkSession
from pyspark.sql import Row
from pyspark.sql.functions import *

warehouse_location = 'spark-warehouse'

spark = SparkSession \
.builder \
.appName("Python Spark SQL Hive integration example") \
.config("spark.sql.warehouse.dir", warehouse_location) \
.enableHiveSupport() \
.getOrCreate()

df= spark.sql("Select fund_date,nav,accnav from fund.rpt_fund")
df1=df.select(substring(trim(df.fund_date),1,7).alias('year'),df.nav,df.accnav)
df2=df1.groupBy("year").mean("nav","accnav").orderBy("year")
df3=df2.toPandas()
df4=df3.set_index(['year'])
df4.plot(kind='bar',rot=30)
## df4.plot(kind='line',rot=30)

或折线图

5.6练习
一、单选题
1. Spark 的四大组件下面哪个不是
A.Spark Streaming
B Mlib
C Graphx
D Spark R

2.spark 1.4 版本的最大变化
A spark sql Release 版本
B 引入 Spark R
C DataFrame
D支持动态资源分配

3. Spark Job 默认的调度模式
A FIFO B FAIR
C 无 D 运行时指定

4.下面哪个不是 RDD 的特点
A. 可分区 B 可序列化 C 可修改 D 可持久化

5. 关于广播变量,下面哪个是错误的
A 任何函数调用 B 是只读的 C 存储在各个节点 D 存储在磁盘或 HDFS

6. 关于累加器,下面哪个是错误的
A 支持加法 B 支持数值类型
C 可并行 D 不支持自定义类型

7.Spark 支持的分布式部署方式中哪个是错误的
A standalone B spark on mesos
C spark on YARN D Spark on local

8.Stage 的 Task 的数量由什么决定
A Partition B Job C Stage D TaskScheduler

9.下面哪个操作是窄依赖
A join B filter
C group D sort

10.下面哪个操作肯定是宽依赖
A map B flatMap
C reduceByKey D sample

11.spark 的 master 和 worker 通过什么方式进行通信的?
A http B nio C netty D Akka

12.下列哪个不是 RDD 的缓存方法
A persist() B Cache()
C Memory()

13.Task 运行在下来哪里个选项中 Executor 上的工作单元
A Driver program B. spark master
C.worker node D Cluster manager

14.hive 的元数据存储在 derby 和 MySQL 中有什么区别
A.没区别 B.多会话 C.支持网络环境 D数据库的区别

15.DataFrame 和 RDD 最大的区别
A.科学统计支持 B.多了 schema
C.存储方式不一样 D.外部数据源支持

二、实战题
1、利用Spark SQL查询MySQL数据库中stud_score,stud_info表,统计学生各科总成绩,并选出前5名的同学。

附录;推荐读物或网站
一、Linux
1、网站:
Linux中国:https://linux.cn
鸟哥的Linux私房菜: http://linux.vbird.org
Linux下载站: http://www.linuxdown.net
Linux公社: http://www.linuxidc.com
2、图书:
《鸟哥的Linux基础学习篇》鸟哥著

二、MySQL
1、网站:
MySQL社区:http://www.mysqlpub.com/
MySQL菜鸟教程: http://www.runoob.com/mysql/mysql-tutorial.html
MySQL官网:http://www.mysql.com/
2、图书
《深入浅出MySQL》唐汉名等著
三、Python
1、网站:
http://www.pythondoc.com/
2、图书:
基础入门:《python编程入门》Toby Donaldson著
《python基础教程》Magnus Lie Hetland著
数据分析:《利用Python进行数据分析》
机器学习:《机器学习实战》
《Python数据挖掘入门与实践》
《Python机器学习》【美】sebastian Raschka
四、Hadoop
1、hadoop官网:
http://www.apache.org/
2、图书:
《hadoop权威指南》第三版
《Hadoop大数据处理》刘军著
《自己动手做大数据系统》飞谷团队著
五、Spark
1、Spark官网:
http://www.apache.org/
2、图书
《Spark 核心技术与高级应用》于俊等著
《spark机器学习》【南非】Nick Pentreath 著
六.R
1.网站:
http://ggplot2.tidyverse.org/reference/
2. 图书:
《R语言实战》作者: Robert I. Kabacoff 译者: 高涛 / 肖楠 / 陈钢
《ggplot2数据分析与图形艺术》[美] 哈德利•威克姆 著;统计之都 译

 

Hadoop入门

第4章 Hadoop入门
4.1 Hadoop大数据处理架构
图4-1 Hadoop大数据处理架构图

从底部开始,Hadoop生态圈由以下内容组成:
• HDFS—— Hadoop生态圈的基本组成部分是Hadoop分布式文件系统(Hadoop Distributed File System-HDFS)。HDFS是一种数据分布式保存机制,数据被保存在计算机集群上。数据写入一次,读取多次。HDFS为HBase等工具提供了数据基础。
• MapReduce——Hadoop的主要执行框架是MapReduce,它是一个分布式、并行处理的编程模型。MapReduce把任务分为map(映射)阶段和reduce(化简)。开发人员使用存储在HDFS中数据(可实现快速存储),编写Hadoop的MapReduce任务。由于MapReduce工作原理的特性, Hadoop能以并行的方式访问数据,从而实现快速访问数据。
• Hbase——HBase是一个建立在HDFS之上,面向列的NoSQL数据库,用于快速读/写大量数据。HBase使用Zookeeper进行管理,确保所有组件都正常运行。
• Zookeeper ——用于Hadoop的分布式协调服务。Hadoop的许多组件依赖于Zookeeper,它运行在计算机集群上面,用于管理Hadoop操作。
• Oozie——Oozie是一个可扩展的工作体系,集成于Hadoop的堆栈,用于协调多个MapReduce作业的执行。它能够管理一个复杂的系统,基于外部事件来执行,外部事件包括数据的定时和数据的出现。
• Pig——它是MapReduce编程的复杂性的抽象。Pig平台包括运行环境和用于分析Hadoop数据集的脚本语言(Pig Latin)。其编译器将Pig Latin翻译成MapReduce程序序列。
• Hive ——Hive类似于SQL高级语言,用于运行存储在Hadoop上的查询语句,Hive让不熟悉MapReduce开发人员也能编写数据查询语句,然后这些语句被翻译为Hadoop上面的MapReduce任务。像Pig一样,Hive作为一个抽象层工具,吸引了很多熟悉SQL而不是Java编程的数据分析师。
Hadoop的生态圈还包括以下几个框架,用来与其它企业融合:
• Sqoop是一个连接工具,用于在关系数据库、数据仓库和Hadoop之间转移数据。Sqoop利用数据库技术描述架构,进行数据的导入/导出;利用MapReduce实现并行化运行和容错技术。
• Flume提供了分布式、可靠、高效的服务,用于收集、汇总大数据,并将单台计算机的大量数据转移到HDFS。它基于一个简单而灵活的架构,并提供了数据流的流。它利用简单的可扩展的数据模型,将企业中多台计算机上的数据转移到Hadoop。
4.2 Hadoop的核心HDFS
图4-2 HDFS架构图

Client:
访问或通过命令行管理HDFS;与NameNode交互,获取文件位置信息;与DataNode交互,读取和写入数据。
NameNode:
Master节点,只有一个,管理HDFS的名称空间和数据块映射信息;配置副本策略;处理客户端请求。
DataNode:
Slave节点,存储实际的数据;执行数据块的读写;汇报存储信息给NameNode。
Secondary NameNode:
辅助NameNode,分担其工作量;定期合并fsimage和fsedits,推送给NameNode;紧急情况下,可辅助恢复NameNode,但Secondary NameNode并非NameNode的热备

4.3 Hadoop的核心MapReduce
MapReduce是hadoop技术的核心,它提供了一种可以利用底层分布式处理环境进行并行出理的模式,并将处理过程分为两个阶段:Map和Reduce。在map阶段,原始数据通过分片处理输入到mapper进行过滤和转换,生成的中间数据在Reduce阶段作为Reducer的输入,经过reducer的聚合处理,得到输出结果。其处理架构如下:
图4-3 MapReduce处理架构图

4.4 HDFS常用命令
在本节将介绍,如何创建或删除HDFS目录、如何把本地文件传输或复制到HDFS上、如何获取HDFS上的文件等。
HDFS的命令格式为:
hadoop fs -cmd
其中cmd为具体命令,为一系列可变参数,请看如下样例:
 查看HDFS上的根目录下的目录或文件:

$ hadoop fs -ls /
drwxr-xr-x - hadoop supergroup 0 2017-02-15 21:29 /ch7
drwxr-xr-x - hadoop supergroup 0 2017-02-25 12:31 /exdata
drwxr-xr-x - hadoop supergroup 0 2017-01-11 21:33 /hbase

 创建目录

$ hadoop fs -mkdir -p /data/tmp
hadoop fs -ls /
drwxr-xr-x - hadoop supergroup 0 2017-02-15 21:29 /ch7
drwxr-xr-x - hadoop supergroup 0 2017-03-26 11:15 /data
hadoop fs -ls /data/tmp

 把linux本地文件上传到HDFS

hadoop fs -put log.txt /data/tmp/
hadoop fs -ls /data/tmp/
-rw-r--r-- 3 hadoop supergroup 34 2017-03-26 11:20 /data/tmp/log.txt

 查看HDFS文件内容

hadoop fs -cat /data/tmp/log.txt
sh: 0: Can't open insert_mysql.sh

 下载HDFS文件

$rm log.txt
$ hadoop fs -get /data/tmp/log.txt

 删除HDFS文件或空目录

$ hadoop fs -rm /data/tmp/log.txt

4.5 Hadoop运行实例
下例为Hadoop2.7.2版本MapReudce示例之WordCount
 查看测试数据

$ cat wordcount.txt
hadoop spark
hadoop hive
hadoop hbase
hadoop pig
spark sql
spark mllib
spark r
spark streaming
spark graphx
scala java
python c
mysql sql

 在HDFS上创建目录

$ hadoop fs -mkdir -p /wordcount/input

 把测试文件上传到HDFS

$hadoop fs -put wordcount.txt /wordcount/input
$ hadoop fs -ls /wordcount/input
-rw-r--r-- 3 hadoop supergroup 138 2017-03-26 15:14 /wordcount/input/wordcount.txt

 运行hadoop一个统计单词频度的程序

hadoop jar /home/hadoop/bigdata/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar wordcount /wordcount/input/wordcount.txt /wordcount/output

 查看运行结果

hadoop fs -cat /wordcount/output/part*

4.6 Hadoop与Hive
Hive是建立在Hadoop之上的数据仓库,由Facebook开发,在某种程度上可以看成是用户编程接口,本身并不存储和处理数据,依赖于HDFS存储数据,依赖MR处理数据。有类SQL语言HiveQL,不完全支持SQL标准,如,不支持更新操作、索引和事务,其子查询和连接操作也存在很多限制。
Hive把HQL语句转换成MR任务后,采用批处理的方式对海量数据进行处理。数据仓库存储的是静态数据,很适合采用MR进行批处理。Hive还提供了一系列对数据进行提取、转换、加载的工具,可以存储、查询和分析存储在HDFS上的数据。
图4-5Hadoop与Hive间的关系:

在4.5节中,我们统计一个文件的字频,使用了一个java现成的包,一般而言,如果需要处理HDFS上文件,需要使用java开发一个相关应用MapReduce来实现;不过有不少任务,我们不一定要使用Java来实现,除了Java,我们还可以使用HQL,尤其对对Java不熟悉的人来说,将带来不少便利,同时,在一定程度上,也大大降处理基于HDFS上文件的门槛,下例是以HQL实现以上需求的一个示例。
###首先在hive中创建1表,然后,把数据导入该表

$ hive
hive> show databases;
OK
default
feigu
sqoop
stg_db
Time taken: 2.72 seconds, Fetched: 4 row(s)
hive> use feigu; ###使用feigu库
###在feigu库中创建表:wordcount
hive>CREATE TABLE wordcount (a1 string ,a2 string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ' ' LINES TERMINATED BY '\n' ;
##导入数据
hive> load data local inpath '/home/hadoop/wordcount.txt' overwrite into table wordcount;

创建一个视图,把两个字段值,放在一列,考虑到有重复值,采用union all

hive> create view v_wordcount (b1) as select a1 from wordcount union all select a2 from wordcount ;
###统计字频
hive> select b1,count(*) from v_wordcount group by b1;
Query ID = hadoop_20170329111745_5f3e13ce-3c76-48c2-933e-14c9936827a2
Total jobs = 1
Launching Job 1 out of 1
Number of reduce tasks not specified. Estimated from input data size: 1
In order to change the average load for a reducer (in bytes):
set hive.exec.reducers.bytes.per.reducer=
In order to limit the maximum number of reducers:
set hive.exec.reducers.max=
In order to set a constant number of reducers:
set mapreduce.job.reduces=
Starting Job = job_1480603936205_0084, Tracking URL = http://master:8088/proxy/application_1480603936205_0084/
Kill Command = /home/hadoop/bigdata/hadoop/bin/hadoop job -kill job_1480603936205_0084
Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 1
2017-03-29 11:18:15,343 Stage-1 map = 0%, reduce = 0%
2017-03-29 11:18:28,879 Stage-1 map = 100%, reduce = 0%, Cumulative CPU 2.03 sec
2017-03-29 11:18:44,870 Stage-1 map = 100%, reduce = 100%, Cumulative CPU 4.75 sec
MapReduce Total cumulative CPU time: 4 seconds 750 msec
Ended Job = job_1480603936205_0084
MapReduce Jobs Launched:
Stage-Stage-1: Map: 1 Reduce: 1 Cumulative CPU: 4.75 sec HDFS Read: 8223 HDFS Write: 113 SUCCESS
Total MapReduce CPU Time Spent: 4 seconds 750 msec
OK
c 1
graphx 1
hadoop 4
hbase 1
hive 1
java 1
mllib 1
mysql 1
pig 1
python 1
r 1
scala 1
spark 6
sql 2
streaming 1
Time taken: 62.681 seconds, Fetched: 15 row(s)
hive>

4.7练习
一、单选题
1. 下面哪个程序负责HDFS数据存储。
a) NameNode b) Jobtracker
c) Datanode
d) secondaryNameNode e) tasktracker
2. HDfS中的block默认保存几份?
a) 3份 b) 2份 c) 1份 d) 不确定

3. 下列哪个程序通常与NameNode在一个节点启动?
a) SecondaryNameNode b) DataNode c) TaskTracker d) Jobtracker

4. HDFS默认Block Size
a) 32MB b) 64MB c) 128MB

5. 下列哪项通常是集群的最主要的性能瓶颈
a) CPU b) 网络 c) 磁盘 d) 内存
二、创建一个文件,然后把该文件复制到HDFS上,然后查看该文件内容。

Python基础

第3章 Python基础

3.1 Python简介

前面我们介绍了Linux操作系统、MySQL数据库。Linux作为大数据平台的基石,其重要性,在这里就不说了;MySQL数据库或推广为其他数据库(实际上其他数据库,如Oracle、DB2、SQL server等)作为存储和管理数据系统,在企业中已运行几十年(当然还将将继续使用下去,在事物处理方面尤其独特优势)积累大量各行各业的数据,这些数据是大数据重要来源。平台有了,数据有了,接下来要做的就是处理和分析这些数据,而Python恰恰是解决这类问题的高手之一,可以毫不夸张的说,Python将把你带进一个数据处理、数据分析的五彩缤纷世界!Python将助你达到一个新境界!
3.1.1 Python概述
Python是做啥用的? 为啥要学Python?在数据处理、数据分析、机器学习等方面除了Python还有其他一个工具,如R,MATLAB等,我们为啥选择Python?1、它开源,2,它易学,3,它强大,4,它与时俱进,它和大数据计算平台SPark的结合,可为强强联合,优势互补、相得益彰!Spark后面我们会重点介绍。
3.1.2 Python简介
Python是一种动态的高级解释性编程语言,简单易学,可读性高,功能强大,目前已广泛用于学校、企业、金融机构等。Python支持面向对象、模块和函数式编程,可以在windows、linux、Unix等操作系统上运行。它有如下主要特征:
 开源:
由荷兰人Guido van Rossum在1989开发的,1991发行第一个正式版本,Python开发的里程碑:
1991年发行Python0.9.0;
1994年发行Python1.0;
2000年发行Python2.0;
2008年发行Python2.6;
2010年发行Python2.7;
2008年发行Python3.0;
2010年发行Python3.3;
2014年发行Python3.4.
从2008年开发有两版本在同时使用,而且这两个版本的代码不是100%兼容的,目前大部分实际使用的一般是Python2.6或Python2.7版本编写。
 跨平台;
Python支持常用的操作系统,如Windows、Linux、Unix和Mac OS,既可在集群,也可运行在一些小设备上。
 面向对象
Python支持面向对象、命令式、函数或过程式编程。
 动态性
Python与JavaScript、PHP、Perl等语言类似,无需预先声明,直接赋值即可。
 缩进感知
Python和大部分编程语言不同,它没有分号、begin、end等标记,使用缩进标记代码块、逻辑块,代替圆括号、方括号或分号等。
 多用途
目前Python已广泛应用于web开发、数据库、图像处理、自然语言处理、网络、操作系统扩展等大型应用程序,也用于高级数据分析、图形处理等领域。
3.1.3 Python重要库


3.1.4 安装配置
上一节介绍的这些库可以用python的安装管理包Anaconda轻松搞定,Anaconda有Linux、windows平台的,这里以2系列的包为例,其它版本安装类似。在Linux下安装,先下载管理包Anaconda2-4.0.0-Linux-x86_64.sh,这是2系列的安装包,3系列的包类似为Anaconda3-4.3.1-Linux-x86_64,然后在Linux命令行下运行:bash Anaconda2-4.0.0-Linux-x86_64.sh,然后按缺省步骤即可,如果要安装其他库,如scipy,只要运行conda install scipy即可。
Python安装完成后,在命令运行python后,启动交互式Python解释器:

其中>>>为提示符,说明已进入python环境,用户可在后面输入python命令、变量等。
Python解释器通过一次运行一条语句。

>>> 2+3
5
>>> print "hello world!"
hello world!
>>> a=10
>>> a
10

退出Python解释器,在提示符下输入quit() 或 Ctrl-D即可。

当然我们也可用IPython,IPython是一种加强的交互式Python解释器,它更加方便、功能更强大、更神奇,它已经成为Python科学计算界的标准配置。启动Ipython,只要在命令行输入ipython。

在IPython我们可以进行计算、编程、运行脚本(用%run 脚本)、copy脚本(%paste)、测试脚本、可视化数据等,甚至可以运行shell命令,当然Tab补全功能、回看历史命令等它都有,方便快捷,功能强大,退出IPython解释器,在命令行输入quit或exit或Ctrl+D。后面我们以IPython为主。

3.2 常量与变量

3.2.1 语言特点
Python语言一个最大特点就是代码缩进与冒号,这是其他任何语言所没有的,这个约束既带来了Python程序的高可读性,也带来了一些其他声音。
Python通过空格或制表符来表示代码之间的层次关系或逻辑关系,而且通过空格缩进是其语法之一,必须严格遵守,否则可能报错或逻辑错误。它不像其它语言如Java、C++、SQL等,这些语句通过花括号或BEGIN...END等关键字来界定代码间的层次关系、开始结束等。
下面我们通过一个实例来进一步了解Python这个特点。

In [1]: a=10

In [2]: if a==10:
...: print("a is ",a) ###缩进4格(IPython在冒号后自动缩4个空格)
...: else:
...: print("a is ",a) ###缩进4格
...: a=a+10 ###缩进4格
...: print("a is ",a)
...:###以下为运行结果
('a is ', 10)
('a is ', 10)
########################################
a=10
a=input()
if a==10:
print("a is ",a) ###缩进4格(IPython在冒号后自动缩4个空格)
else:
print("a is ",a) ###缩进4格
a=a+10 ###缩进4格
print("a is ",a)

【几点说明】
1、冒号(:) 表示一段缩进代码的开始,其后的所有代码都必须缩进相同的量(否则将报错),直到代码块结束。
2、缩进量建议用4个空格,这也是IPython在冒号后自动缩进的空格数,不建议使用Tab制表符。
3、代码缩进量不同,可能结果也会不同。
4、通过冒号和缩进,也同时可少写很多关键字(如then,if,end if之类)。

如果我们把a=a+10语句放在与if语句同一级,运算结果将不同,具体请看下例:

In [8]: a=10

In [9]: if a==10:
...: print("a is ",a)
...: else:
...: print("a is ",a)
...: a=a+10 ###该句于if语句处于相一级,影响a的结果
...: print("a is ",a)
...: ###运行结果
('a is ', 10)
('a is ', 20)
以上代码我们也可以保存为py文件,然后在IPython直接执行运行该文件,非常方便。
In [11]: !cat mytest.py
a=10

if a==10:
print("a is ",a)
else:
print("a is ",a)
a=a+10
print("a is ",a)

In [12]: %run mytest.py ##运行该脚本或import mytest
('a is ', 10)
('a is ', 10)

为了使该脚本有更好的移植性,可在第一行加上一句#!/usr/bin/python
运行.py文件时,python自动创建相应的.pyc文件,如下图,.pyc文件包含目标代码(编译后的代码),它是一种python专用的语言,以计算机能够高效运行的方式表示python源代码。这种代码无法阅读,故我们可以不管这个文件。
python程序是使用名为虚拟机的特殊软件运行的。这个软件模拟计算机,是专为运行在python上而设计的,这让很多.pyc文件无需做任何修改就能在不同的计算机上系统上运行。

类似于shell脚本中的一行。然后,我们可以在命令行运行该该脚本,无需在显式使用Python或IPython解释器。如下例:

feigu@slave001:~$ cat mytest.py
#!/usr/bin/python

a=10

if a==10:
print("a is ",a)
else:
print("a is ",a)
a=a+10
print("a is ",a)
feigu@slave001:~$ python mytest.py ###在命令行运行下,通过python运行该脚本
('a is ', 10)
('a is ', 10)

3.2.2 注释
备注或注释,采用井号(#),注释多行,可以在每行前加#,或前后分别用三个单引号’’’,
如果注释中含中文,一般需要在python文件的最前面加上如下注释:
#-*- coding: utf-8 -*-
python3系列无需这句,因python3缺省字符集就是utf8。
说起这个注释符(#),我们应该不陌生了,shell中的注释也是#,SQL中也可使用。

#!/usr/bin/python
#-*- coding: utf-8 -*-

a=10
###if 判断语句
if a==10:
print("a is ",a)
else:
print("a is ",a)
a=a+10
print("a is ",a)

3.2.3模块导入
模块导入跟我们有啥关系?为什么要进行模块导入?学过Java的朋友,可能不会感到陌生,但对没有接触过Java,Python等新手来说,有这样的疑问,正常,能提出这样的疑问朋友,不错。
模块可用看成是一些对象或函数的集合。前面我们介绍了Python很强大,它确实很强大,他有成千上万的模块可用,不过绝大部分模块都处于“休眠状态”,如果我们要使用这些模块,需要通过import 模块或from 模块 from 函数等方式将它们"激活"。
用import 导入模块,引用模块中的函数时,还需要使用模块.函数的方式,如果你直接用其函数,不行带上模块这个“累赘”,可用采用from 模块 import 函数的方式,以下通过实例进一步说明如何使用以下两种导入方式的异同。
 import 模块
 from 模块 import 函数

In [1]: 1+2
Out[1]: 3

In [2]: max(1,2) ##这些常用函数启动python时就已“激活”
Out[2]: 2

In [3]: sqrt(4) ###使用这个平方根函数,报错,它所在模块没导入
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 sqrt(4)

NameError: name 'sqrt' is not defined

In [4]: import math ###平方根函数在math模块中,导入该模块

In [5]: math.sqrt(4) ###模块.函数,可用使用
Out[5]: 2.0

In [6]: sqrt(4) ###想直接用函数,报错
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 sqrt(4)

NameError: name 'sqrt' is not defined

In [7]: from math import * ###把模块及函数都导入

In [8]: sqrt(4) ###这下简单多了
Out[8]: 2.0

3.2.4变量与常量
在讲shell时,我们介绍了变量,shell中的变量拿来就可用,无需声明它是哪种数据类型,非常简单,Python也“继承”了这个优点,无需声明它是哪种数据类型,不像Java、c++等用一个变量还有先说明它哪种数据类型,否则还不让你用。好东西大家喜欢,scala也沿用了这一优点。
不声明数据类型,并不意味着Python就不需要数据类型,只是它更智能一点,它根据其值来推导。
变量的赋值可以一次一个变量、一次多个变量。

In [9]: a=2
In [10]: type(a)
Out[10]: int
In [11]: b="python"
In [12]: type(b)
Out[12]: str
In [13]: a+b
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 a+b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
In [14]c=range(1,11)
In [15] d=xrange(1,11)
for i in d:
....:print i
###或[i for i in d]

【说明】
xrange 用法与 range 完全相同,所不同的是生成的不是一个list对象,而是一个生成器。要生成很大的数字序列的时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间。
python虽然无需声明变量类型,但它还是区分数据类型的,而且变量的引用无需像shell中变量一样要加上$,它可直接引用。
常量比较好理解,就是一些不变的量,如1,a等。
3.2.5变量如何赋值
变量赋值,通过赋值运算符=,把右边的值赋给左边的变量,具体如下:

应用变量时,需要初始化,否则将报错。

In [18]: v1=2+v0
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 v1=2+v0
NameError: name 'v0' is not defined

In [19]: v0=10 ###初始化
In [20]: v1=2+v0

In [21]: v1
Out[21]: 12

3.2.6多重赋值
python中,可以同时各多个变量赋值:

In [22]: x=10;y=20 ##一次给多个变量赋值
In [23]: a1,a2,a3=1,2,'python' ##一次给多个变量赋值
In [24]: a1,a2,a3
Out[24]: (1, 2, 'python')

3.3 控制语句

控制语句包括条件判断语句、循环语句,控制语句根据条件表达式控制程序的流转。
本章主要介绍
 if条件判断语句
 for循环语句
 while循环语句
 contnue及break语句
3.3.1if判断语句
if语句是一种最常用的条件判断语句,用于判断一个条件,如果成立,则执行紧跟其后的代码块,其语法格式为:
if (表达式):
代码块

if a>0:
print 'a 是正数'

if a>0 and b>0:
print 'a和b都是正数'
if语句也可以有多个分支,如if ...else,if ...elif ... else
a=10
if a>0:
print "a 为整数"
elif a==0:
print "a 为零"
else:
print "a 为负数"

3.3.2 代码块和缩进
对于Python而言代码缩进是一种语法,Python没有像其他语言一样采用{}或者begin...end分隔代码块,而是采用代码缩进和冒号来区分代码之间的层次。缩进的空白数量是可变的,但是在一个模块中所有代码块语句必须包含相同的缩进空白数量,这个必须严格执行,否则将作为语法错误。

有时候代码采用合理的缩进但是缩进的情况不同,代码的执行结果也不同。有相同的缩进的代码表示这些代码属于同一代码块。例如:

# -*- coding: UTF-8 -*-
if 1>0:
print("Hello girl!")
else:
print("Hello boy!")
print("end")
print("=========华丽的分割线===========")
if True:
print("Hello girl!")
else:
print("Hello boy!")
print("end")
运行结果如下:
Hello girl!
end
=========华丽的分割线===========
Hello girl!

【思考】
以上两个print("end")的区别。

3.3.3for循环语句
for循环语句可用于遍历一个集合(如列表、元组等)或迭代器,依次访问集合或迭代器中的每项或每个元素。其语法格式为:
for 变量 in 集合:
代码块

a=range(10) #a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a=range(10)
for i in a:
i+=1
print i ###输出结果为10

当然for 代码块中增加一些控制循环的关键字如continue或break。continue使for循环提前进入下一个循环或下一个迭代(跳过代码块的剩余部分,直接进入下一轮循环),而break是立即跳出for循环。下面通过实例来进一步说明它们的使用。

In [105]: b=[10,20,a,30,a]
In [106]: for i in b:
.....: if i is a:
.....: continue
.....: print i*2
.....:
20
40
60

如果我们把上面的continue关键字换成break,看一下结果:

In [108]: for i in b:
.....: if i is a: ###遍历到第一个a,立即结束for循环语句
.....: break
.....: print i*2
.....:
20
40

3.3.4 while循环语句
while循环定义了一个条件和代码块,只要条件满足,则代码块将一直被执行。其语法格式为:
while 条件:
代码块
下面通过实例来具体说明while的使用。

In [115]: x=10
In [116]: while x左数
Out[20]: 'n'
In [21]: "python"[-1]
Out[21]: 'n'
In [22]: "python"[-3] ##从右往左算第3个元素
Out[22]: 'h'

3.4 序列的基本操作

元组、字符串、列表统称为序列,它们除都有索引外,还有一些共性的操作,如索引、切片、相加、乘法、属员等操作,此外,还有计算序列的长度、最大与最小元素等。
3.4.1 索引
序列中的每个元素都分配一个序号,第一个元素的序号(或编号)为0,第二个元素的序号为1,以此类推,这些序号或编号就是索引。序列的元素可以通过其索引来访问。

In [16]: a="python"
In [17]: a[0]
Out[17]: 'p'
In [18]: a[1]
Out[18]: 'y'
In [19]: a[5]
Out[19]: 'n'

索引由左到右,索引由0开始,然后依次加1;索引也可以从右到左,索引从-1开始、依次减1。

In [20]: a[-1] ##负号表示从右->左数
Out[20]: 'n'
In [21]: "python"[-1]
Out[21]: 'n'
In [22]: "python"[-3] ##从右往左算第3个元素
Out[22]: 'h'

3.4.2 分片
可以通过索引来访问一个元素,是否可以通过索引一次访问多个元素呢?可以的,通过分片操作即可,分片三要素:
1、是索引运算符[]
2、分片的开始与结束位置,即start:stop,stop不在分片范围内。
3、开始与结束通过冒号(:)来分割,start索引所在元素包括在内,但stop索引所在元素不在分片范围内容。start或stop可以省略。具体请看以下实例

In [24]: b=[1,2,3,4,5]
In [25]: b[0:3] ##注意索引3对应元素不在分片范围内。
Out[25]: [1, 2, 3]

In [26]: b[2:] ##查看第3个开始及以后所有元素
Out[26]: [3, 4, 5]

In [27]: b[:] ##表示所有索引
Out[27]: [1, 2, 3, 4, 5]
In [28]: b[-1:]
Out[28]: [5]

In [29]: b[-3:]
Out[29]: [3, 4, 5]

如果只想选择偶数或奇数或选择其他步长,能否实现呢?如果能,又该如何实现呢?请看下例:

In [30]: b[::2] ###第二个冒号后的表示步长
Out[30]: [1, 3, 5]

In [31]: b[0:3:2] ###分片并取步长
Out[31]: [1, 3]

步长不能为零,但是否可以负,大家可尝试一下。
3.4.3 相加
我们知道数字可以相加,字符串也可相加,但数字和字符不能相加。序列是否可以相加呢?如果能相加,又有何限制呢?
可以相加,相加的条件和通常的数字或字符串相加一样,不同类型的不能相加。如元组不能和列表、字符串相加,列表不能和元组、字符串相加,尽管他们都是序列。只要两种相同类型的才能相加。

In [10]: b1=[6,7,8,9,10]

In [11]: b+b1
Out[11]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In [12]: b2=['a','b']

In [13]: b+b2
Out[13]: [1, 2, 3, 4, 5, 'a', 'b']

In [14]: a+b ##不同类型不能相加,a是字符串,b是列表。
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 a+b

TypeError: cannot concatenate 'str' and 'list' objects

3.4.4 乘法
这里的乘法指用一个数字n乘以一个序列生成一个新的序列,新的序列重复原来序列的n次。以下通过实例进一步说明。

In [19]: a*2
Out[19]: 'pythonpython'

In [20]: b*2
Out[20]: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

In [21]: c=(1,2,3)

In [22]: c*3
Out[22]: (1, 2, 3, 1, 2, 3, 1, 2, 3)

3.4.5 最大(小)值及长度
如果我们想知道一个序列的最大或最小值或这个序列的长度,这个长度指序列中元素的个数,尤其这个序列比较长或元素很多时,我们该如何求你?是否有现成的函数?我们知道MySQL里有现成或内置的函数。
Python提供了这方面的函数,如max、min、len等,这些函数是Python的内置函数。或Python自带的函数,我们直接使用即可。以下通过实例来进一步说明这些函数的使用。

In [23]: a
Out[23]: 'python'

In [24]: b
Out[24]: [1, 2, 3, 4, 5]

In [25]: max(b)
Out[25]: 5

In [26]: max(a)
Out[26]: 'y'

In [27]: len(a)
Out[27]: 6

In [28]: len(b)
Out[28]: 5

In [29]: e=[1,2,4,[3,4]] ##这里[3,4]是e的一个元素,故元素个数为4

In [30]: len(e)
Out[30]: 4

3.4.6 排序
对序列进行排序也是一件很平常的事,给序列排序可以sorted(序列)函数,缺省为升序,返回结果为新的有序列表。使用sorted函数不改变原序列的次序。

In [18]: sorted((1,4,5))
Out[18]: [1, 4, 5]

In [19]: sorted("spark")
Out[19]: ['a', 'k', 'p', 'r', 's']

In [20]: sorted((1,5,4,2))
Out[20]: [1, 2, 4, 5]

In [21]: sorted([4,7,2,5])
Out[21]: [2, 4, 5, 7]

In [23]: sorted([4,7,2,5],reverse=True) ##把reverse置为True将使用降序
Out[23]: [7, 5, 4, 2]

In [32]: p1=[7,4,9,6]
In [33]: sorted(p1) ##生成新的列表
Out[33]: [4, 6, 7, 9]

In [34]: p1 ###不会改变原序列的元素的次序
Out[34]: [7, 4, 9, 6]

sorted函数更详细使用可以通过help(sorted)来查询。
3.4.7 元组
上节介绍了序列一些共性方面的内容,如索引、分片等。元组(tuples)是Python中一种常见的数据结构,且不能修改或称为不可变序列,故除了序列常用操作外,没有太多其他操作。以下主要介绍元组的几种创建方法。

In [33]: t1=1,2,4,8 ##用逗号分割一些值,自动创建元组
In [34]: t1
Out[34]: (1, 2, 4, 8)

In [35]: t2="python","spark" ##用逗号分割一些值,自动创建元组
In [36]: t2
Out[36]: ('python', 'spark')

In [37]: t3=() ##元组可以没有值
In [38]: t3
Out[38]: ()

In [39]: t4=(1)
In [40]: t4 ##从结果来看,(1)不是元组,元组应该是使用圆括号
Out[40]: 1

In [41]: t5=(1,) ##如果元组只有一个元素,需要添加一个逗号
In [42]: t5
Out[42]: (1,)

In [43]: t6=tuple([1,2,3,4]) ###tuple函数(序列)生成元组
In [44]: t6
Out[44]: (1, 2, 3, 4)

In [45]: t7=tuple("SparkSQL")
In [46]: t7
Out[46]: ('S', 'p', 'a', 'r', 'k', 'S', 'Q', 'L')

In [47]: t8=tuple(2,3,4) ###tuple的参数要是序列,显然2,3,4没被认为是序列
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 t8=tuple(2,3,4)

TypeError: tuple() takes at most 1 argument (3 given)

In [48]: t9=tuple((2,3,4))
In [49]: t9
Out[49]: (2, 3, 4)

3.4.8 列表
列表为可修改序列,或称为可变序列,故列表上的操作相对比较丰富。这也是列表区别于元组、字符串的主要特点。以下对各种操作进行介绍。
3.4.8.1 列表的创建
列表通常用方括号"[]"或list(序列)函数进行定义,以下通过实例来说明。

In [7]: l1=[2,5,6,9]
In [8]: l1
Out[8]: [2, 5, 6, 9]

In [9]: l2=list("sparksql") ##
In [10]: l2
Out[10]: ['s', 'p', 'a', 'r', 'k', 's', 'q', 'l']

In [11]: l3=list((2,3,6,9))
In [12]: l3
Out[12]: [2, 3, 6, 9]

In [13]: l4=list([3,4,5])
In [14]: l4
Out[14]: [3, 4, 5]

3.4.8.2 添加或移除元素
既然列表是可变序列,这就意味着我们可以对其增删改,修改列表的方法比较多,甚至同一一种操作就有几种实现方法,各种实现方法的场景不同,效率也可能不同。
通过append方法,可以将元素添加到末尾,而用insert可以插入到指定位置,insert比较灵活但计算量比较大。

In [1]: lista=['python','R','hadoop','spark']

In [2]: lista.append('sparkml') ###在末尾添加一个元素

In [3]: lista
Out[3]: ['python', 'R', 'hadoop', 'spark', 'sparkml']

In [4]: lista.insert(3,'hdfs') ##在索引为3的位置上插入hdfs

In [5]: lista
Out[5]: ['python', 'R', 'hadoop', 'hdfs', 'spark', 'sparkml']
insert的逆运算为pop,它可以在指定位置移除元素,remove是按值删除数据,它仅删除第一个匹配的元素。
In [6]: listb=[2,3,1,2,7,5]

In [7]: listb.pop(2) ###这个2是指索引
Out[7]: 1

In [8]: listb
Out[8]: [2, 3, 2, 7, 5]

In [9]: listb.remove(2) ##这个2不是索引,是指元素2,只删除第一个2

In [10]: listb
Out[10]: [3, 2, 7, 5]

判断一个元素是否在列表中,可以用in这个关键字或运算符。

In [11]: 'hdfs'in lista
Out[11]: True

In [12]: 'sparkR'in lista
Out[12]: False

按索引查询比按值查询快很多,在列表中如此,但在后续我们将介绍的字典中,按值查询效率将比列表高很多,字典后面将介绍。
3.4.8.4 排序
对列表进行排序可以sort方法或sorted方法,sort实现就地排序,即改变原列表元素的次序,不生成新的列表;使用sorted排序,原列表元素次序不变,而是生成新的列表。此外sorted可用于可迭代序列,可迭代序列这个概念后面将介绍。

In [19]: listd=[3,5,2,1]
In [20]: listd.sort()
In [21]: listd ##列表的次序发生改变

Out[21]: [1, 2, 3, 5]

In [24]: liste=['red','blue','yellow','gree']
In [25]: listf=sorted(liste)

In [26]: listf
Out[26]: ['blue', 'gree', 'red', 'yellow']

In [27]: liste ##原列表次序未变
Out[27]: ['red', 'blue', 'yellow', 'gree']

数据库排序时通常指明用哪个字段或降序还是升序,在列表中是否可以呢?如果能,如何实现?当然可以,这就是接下来介绍的内容。sort或sorted两个重要参数key和reverse,key就就相当于指定排序的字段,这里key参数是函数,这个函数作用于列表中每个元素,reverse参数为False或True,False为默认的升序,True为降序。以下为具体使用实例。

In [1]: liste=['red','blue','yellow','gree']
In [2]: liste.sort(key=len,reverse=True) ##根据元素长度进行排序
In [3]: liste
Out[3]: ['yellow', 'blue', 'gree', 'red']

In [5]: sorted(liste,key=len) ##排序方式缺省为升序
Out[5]: ['red', 'blue', 'gree', 'yellow']

3.4.9 字符串
字符串是除数字外最重要的数据类型,字符串无处不在。字符串是一种聚合数据结构,我们可以使用索引和切片的方法。

3.4.9.1 字符串索引
字符串索引从0开始(这和我们使用尺子刻度从0开始一样),依次为1,2,,,到len(str)-1等等,当然也可以从右到左,此时,索引从-1开始,-2,-3,,,到-len(str)。如下图:

字符串负索引:

n [23]: s="Python"

In [24]: s[0]
Out[24]: 'P'

In [25]: s[5]
Out[25]: 'n'

In [26]: s[-1]
Out[26]: 'n'

In [27]: s[:2] ##字符串切片
Out[27]: 'Py'

In [28]: s[2:5] ##字符串切片
Out[28]: 'tho'

3.4.9.2 字符串函数
字符串的方法比较多,这里介绍几种常用方法,此外,简单介绍字符串格式化问题。
lower、upper实现字符大小写的转换;replace替换字符;strip去除两侧空格;split将字符串分割成列;字符串的正则表达式(re)。以下通过实例具体说明这些方法或函数的使用。

In [6]: 'Python'.lower
Out[6]:

In [7]: 'Python'.lower()
Out[7]: 'python'

In [8]: 'Python'.upper()
Out[8]: 'PYTHON'

In [9]: 'Python|MySQL|Hadoop|Spark'.split('|')
Out[9]: ['Python', 'MySQL', 'Hadoop', 'Spark']

In [10]: ' Scala '.strip()
Out[10]: 'Scala'

In [11]: 'Python'.replace('P','IP')
Out[11]: 'IPython'

3.4.10 字典
我们知道汉语字典、新华字典、英汉字典等,用这些字典可以根据一个字来查找有关这个字的使用方法,如读音、词语、成语、详细含义等等。这个可通过目录或索引来查找内容有点不同。
这里介绍的Python字典,本质上有点像上面讲的一些字典,就是通过关键字来查询其对应值,相当于Java中的HashMap。
字典(dict)是由多个键(key)和对应的值(value)构成的键-值对组成(键-值对又称为项),每个键和它对应的值之间用冒号(:)隔开,项之间用逗号(,)隔开,而整个字典放在在一个大括号{}里,键是唯一的,但值可以不唯一。字典格式,如:d={'liuhai':26 ,'gaofeng':30}
3.4.10.1 创建字典
字典的创建有多种方法,以下介绍几种常用方法:
1、使用大括号并用冒号分隔键和值,键-值对用逗号分隔。
2、使用dict()函数
3、使用已有的序列。
以下通过一些实例来说明如何创建字典:

In [1]: d1={'a':10 ,'b':20,'c':10}

In [2]: d1
Out[2]: {'a': 10, 'b': 20, 'c': 10}

In [3]: d2={} ##创建空字典

In [4]: d2
Out[4]: {}

In [5]: item=[('zhang',20),('liu',30)]

In [6]: d3=dict(item) ###使用dict函数创建字典

In [7]: d3
Out[7]: {'liu': 30, 'zhang': 20}

In [8]: item1=['red','blue','yellow']

In [9]: item2=[100,200,300]
In [10]: d4=dict(zip(item1,item2))

In [11]: d4
Out[11]: {'blue': 200, 'red': 100, 'yellow': 300}

3.4.10.2 字典的基本操作
 len(d) 返回字典d中键-值对或项的个数;
 d[k] 返回键k对应的值v;
 d.get(k,default_value) 返回字典d中键k对应的值v,没有对应的键k,将返回缺省值(缺省值可自定义);
 k in d 判断k是否在字典d中
以下通过一些具体实例来说明;

In [13]: d4
Out[13]: {'blue': 200, 'red': 100, 'yellow': 300}

In [14]: len(d4)
Out[14]: 3

In [15]: d4['red']
Out[15]: 100

In [16]: d4['gree'] ##如果没有对应的键,则报错
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
in ()
----> 1 d4['gree']

KeyError: 'gree'

In [17]: d4.get('red')
Out[17]: 100

In [18]: d4.get('gree',0) ##如果没有对应的键,则不报错,返回缺省值
Out[18]: 0
In [34]: 'red'in d4 ##键red是否在字典d4中,在则返回True
Out[34]: True

In [35]: 'gree' not in d4
Out[35]: True

 d[k]=v 把键k对应的值修改为v
 del d[k] 删除键为k所在的项;
 d.pop(k) 删除键为k所在的项;
 d.keys() 返回字典d所有的键;
 d.values() 返回字典d所有的值。
以下通过一些具体实例来说明;

In [19]: d4['yellow']=200 ##把键为yellow的值修改为200

In [20]: d4
Out[20]: {'blue': 200, 'red': 100, 'yellow': 200}

In [21]: del d4['yellow'] ##删除键为yellow的项或一个键-值对

In [22]: d4
Out[22]: {'blue': 200, 'red': 100}

In [23]: d4.pop('blue')
Out[23]: 200

In [24]: d4
Out[24]: {'red': 100}
In [26]: d5
Out[26]: {'blue': 200, 'red': 100, 'yellow': 200}

In [27]: d5.keys()
Out[27]: ['blue', 'yellow', 'red']

In [28]: d5.values()
Out[28]: [200, 200, 100]

3.4.11 集合
集合(set)是由唯一元素组成的无序集,集合的创建方式常用的有两种,set函数或用大括号。

In [30]: set([2,3,8,2,6,1]) ##set有去重的功能
Out[30]: {1, 2, 3, 6, 8}

In [31]: {3,3,3,8,6,6}
Out[31]: {3, 6, 8}

集合的基本操作
a&b a与b的交集
a|b a与b的并集
a-b a与b的差
a.issubset(b) 判断a是否为b的子集
a.issuperset(b) 判断a是否为b的超集
以下为具体实例

In [36]: a={1,2,3,4}

In [37]: b={3,4,5,6,7,9}

In [38]: a&b
Out[38]: {3, 4}

In [39]: a|b
Out[39]: {1, 2, 3, 4, 5, 6, 7, 9}

In [40]: a-b
Out[40]: {1, 2}

In [41]: c=a|b

In [42]: c
Out[42]: {1, 2, 3, 4, 5, 6, 7, 9}

In [43]: a.issubset(c)
Out[43]: True

In [44]: c.issuperset(a)
Out[44]: True

3.5 pandas简介

3.5.1 pandas简介
“Pandas经过几个版本的更新,目前已经成为数据清洗、处理和分析的不二选择。”
前面我们介绍了NumPy,它提供了数据处理功能,但需要写很多命令,是否有更加便捷、直观、有效的方法?Pandas就是为此而诞生的。Pandas提供了众多更高级、更直观的数据处理功能,尤其是它的DataFrame数据结构,将给你全新的体验,可以用处理数据库表或电子表格的方式来处理分析数据。
Pandas基于NumPy构建的,它提供的结构或工具,让以NumPy为中心的数据处理、数据分析变得更加简单和高效。
Pandas中两个最常用的对象是Series和DataFrame。使用pandas前,需导入以下内容:

In [1]: import numpy as np
In [2]: from pandas import Series,DataFrame
In [3]: import pandas as pd

3.5.2 pandas数据结构
Pandas主要采用Series和DataFrame两种数据结构。Series是一种类似一维数据的数据结构,由数据(values)及索引(indexs)组成,而DataFrame是一个表格型的数据结构,它有一组有序列,每列的数据可以为不同类型(NumPy数据组中数据要求为相同类型),它既有行索引,也有列索引。
3.5.3 Series
上章节我们介绍了多维数组(ndarray),当然,它也包括一维数组,Series类似一维数组,为啥还要介绍Series呢?或Series有哪些特点?
Series一个最大特点就是可以使用标签索引,序列及ndarray也有索引,但都是位置索引或整数索引,这种索引有很多局限性,如根据某个有意义标签找对应值?切片时采用类似[2:3]的方法,只能取索引为2这个元素等等,无法精确定位。
Series的标签索引(它位置索引自然保留)使用起来就方便多了,而且定位也更精确,不会产生歧义。举例说明。

In [1]: import numpy as np
In [2]: from pandas import Series,DataFrame
In [3]: import pandas as pd

In [4]: s1=Series([1,3,6,-1,2,8])

In [5]: s1
Out[5]:
0 1
1 3
2 6
3 -1
4 2
5 8
dtype: int64

In [6]: s1.values
Out[6]: array([ 1, 3, 6, -1, 2, 8])

In [7]: s1.index
Out[7]: RangeIndex(start=0, stop=6, step=1)
###创建Series时,自定义索引或称为标签索引
In [8]: s2=Series([1,3,6,-1,2,8],index=['a','c','d','e','b','g'])

In [9]: s2
Out[9]:
a 1
c 3
d 6
e -1
b 2
g 8
dtype: int64

In [10]: s2['a'] ###根据标签索引找对应值
Out[10]: 1
In [11]: s2[['a','e']] ###根据标签索引找对应值
Out[11]:
a 1
e -1
dtype: int64

当然,Series除了标签索引外,还有其它很多优点,如运算的简洁:

In [15]: s2[s2>1]
Out[15]:
c 3
d 6
b 2
g 8
dtype: int64

In [16]: s2*10
Out[16]:
a 10
c 30
d 60
e -10
b 20
g 80
dtype: int64

3.5.4 DataFrame
DataFrame除了索引有位置索引也有标签索引,而且其数据组织方式与MySQL的表极为相似,除了形式相似,很多操作也类似,这就给我们操作DataFrame带来极大方便。这些是DataFrame特色的一小部分,它还有比数据库表更强大的功能,如强大统计、可视化等等。
DataFrame几要素:index、columns、values等,columns就像数据库表的列表,index是索引,当然values就是值了。

In [18]:
####自动生成一个3行4列的DataFrame,并定义其索引(如果不指定,缺省为整数索引)####及列名
d1=DataFrame(np.arange(12).reshape((3,4)),index=['a','b','c'],columns=['a1','a2','a3','a4'])

In [19]: d1
Out[19]:
a1 a2 a3 a4
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11

In [20]: d1.index ##显示索引
Out[20]: Index([u'a', u'b', u'c'], dtype='object')

In [21]: d1.columns ##显示列名
Out[21]: Index([u'a1', u'a2', u'a3', u'a4'], dtype='object')

In [22]: d1.values ##显示值
Out[22]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])

3.5.4.1 生成DataFrame
生成DataFrame有很多,比较常用的有导入等长列表、字典、numpy数组、数据文件等。

In [33]: data={'name':['zhanghua','liuting','gaofei','hedong'],'age':[40,45,50,46],'addr':['jianxi','pudong','beijing','xian']}

In [34]: d2=DataFrame(data)

In [35]: d2
Out[35]:
addr age name
0 jianxi 40 zhanghua
1 pudong 45 liuting
2 beijing 50 gaofei
3 xian 46 hedong

In [36]: d3=DataFrame(data,columns=['name','age','addr'],index=['a','b','c','d'])

In [37]: d3
Out[37]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian

3.5.4.2 获取数据
获取DataFrame结构中数据可以采用obj[]操作、查询、obj.ix[]等命令。

In [8]: data={'name':['zhanghua','liuting','gaofei','hedong'],'age':[40,45,50,46],'addr':['jianxi','pudong','beijing','xian']}
###把字典数据转换为DataFrame,并指定索引
In [9]: d3=DataFrame(data,columns=['name','age','addr'],index=['a','b','c','d'])
In [10]: d3
Out[10]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian

In [11]: d3[['name','age']] ##选择列
Out[11]:
name age
a zhanghua 40
b liuting 45
c gaofei 50
d hedong 46

In [12]: d3['a':'c'] ##选择行
Out[12]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing

In [13]: d3[1:3] ##选择行(利用位置索引)
Out[13]:
name age addr
b liuting 45 pudong
c gaofei 50 beijing
In [14]: d3[d3['age']>40] ###使用过滤条件
Out[14]:
name age addr
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
obj.ix[indexs,[columns]]可以根据列或索引同时进行过滤,具体请看下例:
In [16]: d3.ix[['a','c'],['name','age']]
Out[16]:
name age
a zhanghua 40
c gaofei 50

In [17]: d3.ix['a':'c',['name','age']]
Out[17]:
name age
a zhanghua 40
b liuting 45
c gaofei 50

In [18]: d3.ix[0:3,['name','age']]
Out[18]:
name age
a zhanghua 40
b liuting 45
c gaofei 50

pandas除了可通过ix确定行列位置外,还可以通loc、iloc来定位或查找需要的数据,这三者主要区别可参考如下示例

import pandas as pd    
data = [[1,2,3],[4,5,6]]    
index = [0,1]    
columns=['a','b','c']    
df = pd.DataFrame(data=data, index=index, columns=columns)    
 
#1. loc——通过行标签索引行数据  
df.loc[1]    
'''''''  
a    4  
b    5  
c    6  
'
''    
 
#1.2 loc[‘d’]表示索引的是第’d’行(index 是字符)  
import pandas as pd    
data = [[1,2,3],[4,5,6]]    
index = ['d','e']    
columns=['a','b','c']    
df = pd.DataFrame(data=data, index=index, columns=columns)    
df.loc['d']    
'''''''  
a    1  
b    2  
c    3  
'
''    
 
#1.3 如果想索引列数据,像这样做会报错  
print df.loc['a']    
'''''''  
KeyError: '
the label [a] is not in the [index]'  
'
''    
 
#1.4 loc可以获取多行数据  
print df.loc['d':]    
'''''''  
   a  b  c  
d  1  2  3  
e  4  5  6  
'
''    
 
#1.5 loc扩展——索引某行某列  
 
print df.loc['d',['b','c']]    
'''''''  
b    2  
c    3  
'
''    
 
#1.6 loc扩展——索引某列  
 
print df.loc[:,['c']]    
'''''''  
   c  
d  3  
e  6  
'
''    
 
##当然获取某列数据最直接的方式是df.[列标签],但是当列标签未知时可以通过这种方式##获取列数据。  
 
#需要注意的是,dataframe的索引[1:3]是包含1,2,3的,与平时的不同。  
 
#2. iloc——通过行号获取行数据  
 
#2.1 想要获取哪一行就输入该行数字  
 
df.iloc[1]    
'''''''  
a    4  
b    5  
c    6  
'
''    
 
#2.2 通过行标签索引会报错  
 
print df.iloc['a']    
'''''''  
TypeError: cannot do label indexing on <class '
pandas.core.index.Index'> with these indexers [a] of <type 'str'>  
'
''    
 
#2.3 同样通过行号可以索引多行  
 
df.iloc[0:]    
'''''''  
   a  b  c  
d  1  2  3  
e  4  5  6  
'
''    
 
#2.4 iloc索引列数据  
 
df.iloc[:,[1]]    
'''''''  
   b  
d  2  
e  5  
'
''    
 
#3. ix——结合前两种的混合索引  
 
#3.1 通过行号索引  
 
df.ix[1]    
'''''''  
a    4  
b    5  
c    6  
'
''    
 
#3.2 通过行标签索引  
 
df.ix['e']    
'''''''  
a    4  
b    5  
c    6  
'
''

3.5.4.3 修改数据
我们可以像操作数据库表一样操作DataFrame,删除数据,插入数据、修改字段名、索引名、修改数据等,以下通过一些实例来说明。

In [9]: data={'name':['zhanghua','liuting','gaofei','hedong'],'age':[40,45,50,46],'addr':['jianxi','pudong','beijing','xian']}

In [10]: d3=DataFrame(data,columns=['name','age','addr'],index=['a','b','c','d'])

In [11]: d3
Out[11]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
In [12]: d3.drop('d',axis=0) ###删除行,如果欲删除列,使axis=1即可
Out[12]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
In [13]: d3 ###从副本中删除,原数据没有被删除
Out[13]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
###添加一行,注意需要ignore_index=True,否则会报错
In [14]: d3.append({'name':'wangkuan','age':38,'addr':'henan'},ignore_index=True)
Out[14]:
name age addr
0 zhanghua 40 jianxi
1 liuting 45 pudong
2 gaofei 50 beijing
3 hedong 46 xian
4 wangkuan 38 henan

In [15]: d3 ###原数据未变
Out[15]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
###添加一行,并创建一个新DataFrame
In [16]: d4=d3.append({'name':'wangkuan','age':38,'addr':'henan'},ignore_index=True)
In [17]: d4
Out[17]:
name age addr
0 zhanghua 40 jianxi
1 liuting 45 pudong
2 gaofei 50 beijing
3 hedong 46 xian
4 wangkuan 38 henan

In [18]: d4.index=['a','b','c','d','e'] ###修改d4的索引

In [19]: d4
Out[19]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
e wangkuan 38 henan
In [20]: d4.ix['e','age']=39 ###修改索引为e列名为age的值

In [21]: d4
Out[21]:
name age addr
a zhanghua 40 jianxi
b liuting 45 pudong
c gaofei 50 beijing
d hedong 46 xian
e wangkuan 39 henan

3.5.4.4 汇总统计
Pandas有一组常用的统计方法,可以根据不同轴方向进行统计,当然也可按不同的列或行进行统计,非常方便。
常用的统计方法有:


(表7-1 Pandas统计方法)

以下通过实例来说明这些方法的使用

from pandas import DataFrame
import numpy as np
import pandas as pd
inputfile = '/home/hadoop/data/stud_score.csv'
data = pd.read_csv(inputfile)
#其他参数,
###header=None 表示无标题,此时缺省列名为整数;如果设为0,表示第0行为标题
###names,encoding,skiprows等
#读取excel文件,可用read_excel
In [7]: df=DataFrame(data)

In [8]: df.head(3) ###显示前3行
Out[8]:
stud_code sub_code sub_nmae sub_tech sub_score stat_date
0 2.015101e+09 10101.0 数学分析 NaN 90.0 NaN
1 2.015101e+09 10102.0 高等代数 NaN 88.0 NaN
2 2.015101e+09 10103.0 大学物理 NaN 67.0 NaN

In [9]: df.count()
Out[9]:
stud_code 121
sub_code 121
sub_nmae 121
sub_tech 0
sub_score 121
stat_date 0
dtype: int64

In [10]: df['sub_score'].describe() ##汇总学生各科成绩
Out[10]:
count 121.000000
mean 78.561983
std 12.338215
min 48.000000
25% 69.000000
50% 80.000000
75% 89.000000
max 98.000000
Name: sub_score, dtype: float64

In [11]: df['sub_score'].std() ##求学生成绩的标准差
Out[11]: 12.338214729032906

注:DataFrame数据结构的函数或方法有很多,大家可以通过df.[Tab键]方式查看,具体命令的使用方法,如df.count(),可以在Ipython命令行下输入:?df.count() 查看具体使用,退出帮助界面,按q即可。

3.6 函数

3.6.1函数的定义
说到函数大家应该不陌生,函数从初中就开始了,函数的作用我们也大都有所了解,利用函数极大提高了我们工作的效率,一个个函数就像软件行业的一块块芯片,插上接口就可使用。所以函数很好的解决程序的移植性、重用性、复制性、易用性和可靠性等等。
Python的函数同样有这些特性,Python有很多内置函数,如max、min、abs等,甚至还有很多内置机器算法或模型、机器学习库等,如sklearn库中的支持向量机、keras中的神经网络等等,这里就不展开说了,这章主要介绍自定义函数的创建及使用。
 Python函数定义的格式:
def 函数名(参数1,参数2,...):
函数体
return 表达式或变量
【说明】
1、参数可分为位置参数、关键参数或默认参数或可选参数;
2、返回值可以是表达式、变量,甚至没有return语句,此时返回None。
 Python函数的调用:
函数名(参数1,参数2,....)
下面是定义、调用函数的一些实例

In [1]: def myfun(x,y): ###定义函数
...: return x+y
...:

In [2]: myfun(2,6) ###调用函数
Out[2]: 8
In [5]: def myfun01(x,y,z=1): ##定义一个含缺省参数(z)的函数
...: if z==1:
...: return x+y
...: else:
...: return (x+y)*z
...:

In [6]: myfun01(2,6) ###调用函数时,没有输入z参数,则取其缺省值
Out[6]: 8

In [7]: myfun01(2,6,10) ###调用函数时,输入z参数,则取其输入值
Out[7]: 80

3.6.2 函数的返回值
我们在介绍Shell的函数时,发现其局限很多,通过return只能返回[0,255]间的某个整数,如果要返回其它值,需要通过echo的方式,然后再通过运行该函数来获取返回值,大大限制了函数的表现力。Python的函数返回值,是否也是这样呢?
Python的函数强大多了,也方便多了!从输入参数可以看出它不同一般的很多优点,其返回值同样有很多不一样的地方。
不但可以返回多个值,还可有多种形式,举例如下:

In [8]: def myfun02():
...: a=1
...: b=3
...: c=8
...: return a,b,c
...: myfun02()
...:
Out[8]: (1, 3, 8) ###返回多个值,而且很方便

In [9]: x,y,z=myfun02()

In [10]: print x,y,z ###轻松返回多个值,而且轻松获取返回值
1 3 8

3.6.3 变量的作用域
前面我们提到模块及函数,一个模块中可能有很多函数和变量,当然在一个函数中也可能涉及很多变量或嵌套函数等等问题,如此,变量的活动范围(或作用范围)就是一个重要问题。改变变量作用范围的有def、class、lambda等定义函数或类的语句,循环语句不影响变量的作用域。
我们先看一个示例:

#testlocalvar.py
import math
def dist(x,y):
a,b=0,0
d2=(x-a)**2+(y-b)**2
d=math.sqrt(d2)
return d

dist(3,4) ###调用函数
#print(a) ###应用局部变量a将报错

局部变量只是在其所属的函数内部使用,在函数外,不能访问函数的局部变量,函数结束时,其局部变量将被自动删除。
在一个模块中除了函数内部的变量外,一般也有函数之外的变量,这部分变量可以被其他函数访问,但能否被被其他函数修改呢?我们来看一个示例:

#testglobalvar.py
name='python'

def select_tool():
print('select the tool is:'+name)

def select_other(a):
name=a

select_other('java')
def select_tool()

运行结果是:
select the tool is:python
而不是:
select the tool is:java
上例中之所以结果非我们期待的java,究其原因,就是函数select_other中name是一个局部变量,该局部变量,执行函数select_other()立即被删除,如要出现我们期望的结果,该如何设计?
方法之一,可以在select_other函数中声明name为全局变量,如下例:

#testglobalvar.py
name='python'

def select_tool():
print('select the tool is:'+name)

def select_other(a):
global name
name=a

select_other('java')
def select_tool()

运行结果是:
select the tool is:java
3.6.4 函数也是对象
在Python中,有一句话非常有代表性:“万物皆对象”,一个字符是对象,一个数字也是对象,更不用说数据结构、模块、函数了,何以见得?既然是对象,那应该就有属性或方法,难道一个字符也有属性和方法?下面看几个实例。

In [18]: a='b'

In [19]: a. ###a.然后按Tab键,就可以看到a具有的方法或属性
a.capitalize a.endswith a.isalnum a.istitle a.lstrip a.rjust a.splitlines a.translate
a.center a.expandtabs a.isalpha a.isupper a.partition a.rpartition a.startswith a.upper
a.count a.find a.isdigit a.join a.replace a.rsplit a.strip a.zfill
a.decode a.format a.islower a.ljust a.rfind a.rstrip a.swapcase
a.encode a.index a.isspace a.lower a.rindex a.split a.title

In [19]: b=2

In [20]: b. ###b.然后按Tab键,就可以看到b具有的方法或属性
b.bit_length b.conjugate b.denominator b.imag b.numerator b.real

这是Python非常神奇、非常强大的特点之一。后续大家将看到更多有趣的地方。
万物皆对象有啥好处?好处很多,第一,保证了python对象的一致性;第二,增强python的灵活性;第三,提供了使用的方便性(如通过.Tab就可方便查看对象的属性和方法等),等等。下面介绍一个实际应用,既然函数也是对象,当然就可以把它作为其它对象来对待,如字符或数字或参数之类。函数可以作为一个参数,传给另一个函数;函数也可作为函数的返回值。

In [11]: def sum1(x,y):
....: return x+y
....: def max1(a,f): ##f就是一个函数参数
....: return max(a,f) ##函数max作为返回值
....: max1(10,sum1(20,30))
....:
Out[11]: 50

它的好处,现在你可能感觉不到,不过scala已经在大面积使用这一方法。

3.7 数据可视化

无论是大数据、还是小数据、也不管通过统计还是挖掘或机器学习,人们最终想看到的数据,越直观越好,所以这个就涉及到一个数据的可视化问题,而python或pandas的数据可视化功能很强大,可画的种类多,也非常便捷,这是一般数据库软件和开发工具目前所欠缺的。以下我们通过两个实例来说明利用python的matplotlib或pandas实现数据的可视化。
下例利用matplotlib实现数据的可视化

In [1]: %paste
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif']=['SimHei'] ###显示中文
plt.rcParams['axes.unicode_minus']=False ##防止坐标轴上的-号变为方块
x = np.linspace(0, 10, 100)
y = np.sin(x)
y1 = np.cos(x)
##绘制一个图,长为10,宽为6(默认值是每个单位80像素)
plt.figure(figsize=(10,6))
###在图列中自动显示$间内容
plt.plot(x,y,label="$sin(x)$",color="red",linewidth=2)
plt.plot(x,y1,"b--",label="$cos(x^2)$") ###b(blue),--线形
plt.xlabel(u"X值") ##X坐标名称,u表示unicode编码
plt.ylabel(u"Y值")
plt.title(u"三角函数图像") ##t图名称
plt.ylim(-1.2,1.2) ##y上的max、min值
plt.legend() ##显示图例
plt.savefig('fig01.png') ##保持到当前目录
plt.show()

以下是运行结果:


(图 9-2 matplotlib数据可视化)

3.8 数据地图显示

下例通过matplotlib及mpl_toolkits.basemap实现地图数据的可视化。

# -*- coding: utf-8 -*-

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.sans-serif']=['SimHei']
#============================================# read data
names = []
pops = []
lats = []
lons = []
countries = []
for line in file("/home/hadoop/data/bigdata_map/china_city_jobs_stat.csv"):
info = line.split(',')
names.append(info[0])
pops.append(float(info[1]))
lat = float(info[2][:-1])
if info[2][-1] == 'S': lat = -lat
lats.append(lat)
lon = float(info[3][:-1])
if info[3][-1] == 'W': lon = -lon + 360.0
lons.append(lon)
country = info[4]
countries.append(country)

#============================================
#lat0 = 35;lon0 = 120;change = 25;
lat0 = 30;lon0 = 120;change = 26;
lllat=lat0-change; urlat=lat0+change;
lllon=lon0-change; urlon=lon0+change;

map = Basemap(ax=None,projection='stere',lon_0=(urlon + lllon) / 2,lat_0=(urlat + lllat) / 2,llcrnrlat=lllat, urcrnrlat=urlat,llcrnrlon=lllon,urcrnrlon=urlon,resolution='f')
# draw coastlines, country boundaries, fill continents.
map.drawcoastlines(linewidth=0.25)
map.drawcountries(linewidth=0.25)
# draw the edge of the map projection region (the projection limb)
map.drawmapboundary(fill_color='#689CD2')
# draw lat/lon grid lines every 30 degrees.
#map.drawmeridians(np.arange(0,360,30))
#map.drawparallels(np.arange(-90,90,30))
# Fill continent wit a different color
map.fillcontinents(color='green',lake_color='#689CD2',zorder=0)
# compute native map projection coordinates of lat/lon grid.
shapefilepath = '/home/hadoop/data/bigdata_map/map/map'
map.readshapefile(shapefilepath,'city') #添加街道数据
x, y = map(lons, lats)

max_pop = max(pops)
# Plot each city in a loop.
# Set some parameters
size_factor = 80.0
y_offset = 15.0
rotation = 30
for i,j,k,name in zip(x,y,pops,names):
size = size_factor*k/max_pop
cs = map.scatter(i,j,s=size,marker='o',color='yellow')
plt.text(i,j+y_offset,name,rotation=rotation,fontsize=1)

plt.title(u'中国大数据主要城市需求分布图(2017-03-17)')
plt.show()

运行结果如下图:


(图9-3 中国大数据需求分别图)

3.9 图像处理

3.9.1PIL常用操作
这节我们将介绍如何Python库PIL(Python Imaging Library,图像处理类库)提供的图像处理功能,以及大量有用的基本图像操作,比如图像缩放、裁剪、旋转、颜色转换等。
利用 PIL 中的函数,我们可以从大多数图像格式的文件中读取数据,然后写入最常见的图像格式文件中。PIL 中最重要的模块为 Image。要读取一幅图像,可以使用:

import PIL
from PIL importImage
im = Image.open('timg.png')
im.show() ###显示图片

【说明】如果ubuntu系统无法显示图像,看是否已安装imagemagick,如果没有,通过sudo apt-get install imagemagick来安装。
上述代码的返回值 im 是一个 PIL 图像对象
图像的颜色转换可以使用 convert() 方法来实现。要读取一幅图像,并将其转换成灰度图像,只需要加上 convert('L'),如下所示:

from PIL importImage
im1=Image.open('timg.png').convert('L')
im1.show()

使用 PIL 可以很方便地创建图像的缩略图。thumbnail() 方法接受一个元组参数(该参数指定生成缩略图的大小),然后将图像转换成符合元组参数指定大小的缩略图。例如,创建最长边为 128 像素的缩略图,可以使用下列命令:
im.format, im.size, im.mode ##查看图像信息
('JPEG', (128, 128), 'RGB')
im.thumbnail((128,128)) ###设置目前大小
im.save("tenna_bak.gif","GIF") ###保存图片,并转换格式
如何图片剪辑?我们可以通过crop方法,先设定剪辑范围,然后使用crop方法即可:

box=(100,100,130,130) ##设置图片剪辑区
region=im.crop(box) ### region是一个新的图像对象

如何选择图像?我们可以通过
box=(100,100,130,130) ##设置图片剪辑区
如何旋转一个图片?我们可以通过函数rotate

im3=im.rotate(45)
im3.show()

如何实现图像的模糊处理?可以使用ImageFilter方法,具体请看如下代码:

from PIL import ImageFilter
im4 = im.filter(ImageFilter.BLUR) ##模糊滤波,处理之后的图像会整体变得模糊
im4.show()

3.9.2随机生成验证码
PIL的ImageDraw提供了一系列绘图方法,以下利用该方法,随机生成字母验证码图

from PIL import Image,ImageDraw, ImageFont, ImageFilter

import random

# 随机字母:
def rndChar():
return chr(random.randint(65, 90))

# 随机颜色1:
def rndColor():
return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))

# 随机颜色2:
def rndColor2():
return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))

# 240 x 60:
width = 60 * 4
height = 60
image = Image.new('RGB', (width, height), (255, 255, 255))
# 创建Font对象:
font1= ImageFont.truetype('/home/hadoop/anaconda2/lib/python2.7/site-packages/matplotlib/mpl-data/fonts/ttf/Arial.ttf', 36)
# 创建Draw对象:
draw = ImageDraw.Draw(image)
# 填充每个像素:
for x in range(width):
for y in range(height):
draw.point((x, y), fill=rndColor())
# 输出文字:
for t in range(4):
draw.text((60 * t + 10, 10), rndChar(), font=font1, fill=rndColor2())
# 模糊:
image = image.filter(ImageFilter.BLUR)
#显示图形
image.show()

3.9.3图像数组表示
NumPy是非常有名的 Python 科学计算工具包,其中包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性代数函数。NumPy 中的数组对象几乎贯穿用于本书的所有例子中 1 数组对象可以帮助你实现数组中重要的操作,比如矩阵乘积、转置、解方程系统、向量乘积和归一化,这为图像变形、对变化进行建模、图分类、图像聚类等提供了基础。PyLab 实际上包含 NumPy 的一些内容,如数组类型。
先前的例子中,当载入图像时,我们通过调用 array() 方法将图像转换成 NumPy 的数组对象,但当时并没有进行详细介绍。NumPy 中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表(或者是列表的列表),但是数组中所有的元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。
对于图像数据,下面的例子阐述了这一点:

im = array(Image.open('empire.jpg'))
print im.shape, im.dtype

结果显示如下:

(220, 142, 3) uint8
im = array(Image.open('empire.jpg').convert('L'),'f')
print im.shape, im.dtype

结果显示如下:
(220, 142) float32
每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。关于更多数据类型选项,可以参考图书 [24]。注意,由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。
数组中的元素可以使用下标访问。位于坐标 i、j,以及颜色通道 k 的像素值可以像下面这样访问:
value = im[i,j,k]
多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。下面是有关灰度图像的一些例子:

im[i,:] = im[j,:] # 将第 j 行的数值赋值给第 i 行
im[:,i] = 100 # 将第 i 列的所有数值设为100
im[:100,:50].sum() # 计算前100 行、前 50 列所有数值的和
im[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
im[i].mean() # 第 i 行所有数值的平均值
im[:,-1] # 最后一列
im[-2,:] (or im[-2]) # 倒数第二行

注意,示例仅仅使用一个下标访问数组。如果仅使用一个下标,则该下标为行下标。注意,在最后几个例子中,负数切片表示从最后一个元素逆向计数。我们将会频繁地使用切片技术访问像素值,这也是一个很重要的思想。

3.10 操作MySQL数据库

人工智能最重要的动力就是数据,没有充分数据的人工智能是毫无意义的,数据越大人工智能将越聪明,所以数据是非常重要的。因此整合各种数据的能力也是各种开发工具非常重视的,Python在这方面功能很强大。这一章我们将介绍如何利用Python存取数据库中的数据。
目前企业数据大都存储在数据库中,如MySQL、Oracle、DB2等关系型数据库,这些数据库目前使用非常广泛,由于其独特优势,未来还将大量使用;但随着数据量、数据种类的不断增长,目前非关系型数据库也越来越普及了,如MongoDB、Redis、HBase等等,这些数据库有时又称为NoSQL型数据库。所有这些数据是目前大数据的主要源头,因此,如何使用抽取、整合、处理及分析这些数据是非常重要。
这章主要内容:
 操作MySQL

Python使用MySQL非常简单,先导入python有关mysql的驱动模块,然后建立与数据库的连接即可。
以下通过实例来说明。

In [2]: import numpy as np
In [3]: import pandas as pd
In [4]: from pandas import DataFrame
In [5]: import MySQLdb

In [6]: conn= MySQLdb.connect(host='slave02',port=3306,user='feigu', passwd='feigu', db='testdb',charset='utf8')

In [7]: data= pd.read_sql('select * from stud_score', conn)
In [8]: df=DataFrame(data)

In [9]: df.count()

3.11 初学者最易犯的一些错误

Python 以其简单易懂的语法格式与其它语言形成鲜明对比,初学者遇到最多的问题就是不按照 Python 的规则来写,即便是有编程经验的程序员,也容易按照固有的思维和语法格式来写 Python 代码,以下小结一下初学者最易犯的几个错误,供大家参考。
注:以上代码都是基于 Python3 的,在 Python2 中即使是同样的代码出现的错误也不尽一样。
1、忘记冒号:
在 if、elif、else、for、while、class、def 语句后面忘记添加 “:”

spam=26
if spam > 20
print('Hello!')

报错:SyntaxError: invalid syntax
订正:

spam=26
if spam > 20:
print('Hello!')

2、误用 “=” 做等值比较
“=” 是赋值操作,而判断两个值是否相等是 “==”

if spam = 42:
print('Hello!')

报错:SyntaxError: invalid syntax

3、使用错误的缩进
Python用缩进区分代码块,常见的错误用法:

print('Hello!')
print('Happy new year!')

报错:IndentationError: unexpected indent
说明:同一个代码块中的每行代码都必须保持一致的缩进量

if spam == 42:
print('Hello!')
print('Howdy!')

报错:IndentationError: expected an indented block
说明:“:” 后面要使用缩进
4、变量没有定义

if age == 42:
print('Hello!')

报错:NameError: name 'age' is not defined

5、获取列表元素索引位置忘记调用 len 方法
通过索引位置获取元素的时候,忘记使用 len 函数获取列表的长度。

spam = ['cat', 'dog', 'mouse']
for i in range(spam):
print(spam[i])

报错:TypeError: 'list' object cannot be interpreted as an integer
订正:

spam = ['cat', 'dog', 'mouse']
for i in range(len(spam)):
print(spam[i])
cat
dog
mouse

可用更符合python风格的写法是用 enumerate

spam = ['cat', 'dog', 'mouse']
for i, item in enumerate(spam):
print(i, item)
0 cat
1 dog
2 mouse

6、修改字符串
字符串一个序列对象,支持用索引获取元素,但它和列表对象不同,字符串是不可变对象,不支持修改。

spam = 'I have a pet cat.'
spam[5] = 'r'
print(spam)

报错: in ()
1 spam = 'I have a pet cat.'
----> 2 spam[5] = 'r'
3 print(spam)

TypeError: 'str' object does not support item assignment
订正:

spam = 'I have a pet cat.'
spam = spam[:13] + 'r' + spam[14:]
print(spam)
I have a pet rat.

7、字符串与非字符串连接

num_eggs = 12
print('I have ' + num_eggs + ' eggs.')

报错:TypeError: must be str, not int
说明:字符串与非字符串连接时,必须把非字符串对象强制转换为字符串类型。

num_eggs = 12
print('I have ' + str(num_eggs) + ' eggs.')
I have 12 eggs.

或者使用字符串的格式化形式

num_eggs = 12
print('I have %s eggs.' % (num_eggs))
I have 12 eggs.

8、使用错误的索引位置

spam = ['cat', 'dog', 'mouse']
print(spam[3])

报错:
使用错误的索引位置1 spam = ['cat', 'dog', 'mouse']
----> 2 print(spam[3])
IndexError: list index out of range

说明:列表对象的索引是从0开始的,第3个元素应该是使用 spam[2] 访问。

9、字典中使用不存在的键

spam = {'cat': 'Zophie', 'dog': 'Basil', 'mouse': 'Whiskers'}
print('The name of my pet zebra is ' + spam['zebra'])

报错:KeyError: 'zebra'
说明:在字典对象中访问 key 可以使用 [],但是如果该 key 不存在,就会报错。
订正:正确的方式应该使用 get 方法,如果key 不存在,get 默认返回 None。

spam = {'cat': 'Zophie', 'dog': 'Basil', 'mouse': 'Whiskers'}
spam.get('zebra')

10、用关键字做变量名

class = 'algebra'

报错:SyntaxError: invalid syntax
说明:在 Python 中不允许使用关键字作为变量名。Python3 一共有33个关键字。关键字有:

import keyword
print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

11、函数中局部变量赋值前被使用

someVar = 42

def myFunction():
print(someVar)
someVar = 100

myFunction()

报错:UnboundLocalError: local variable 'someVar' referenced before assignment
说明:当函数中有一个与全局作用域中同名的变量时,它会按照 LEGB 的顺序查找该变量,如果在函数内部的局部作用域中也定义了一个同名的变量,那么就不再到外部作用域查找了。因此,在 myFunction 函数中 someVar 被定义了,所以 print(someVar) 就不再外面查找了,但是 print 的时候该变量还没赋值,所以出现了 UnboundLocalError

12、使用自增 “++” 自减 “--”

spam = 0
spam++

报错:SyntaxError: invalid syntax
说明:Python 中没有自增自减操作符,如果你是从C、Java转过来的话,你可要注意了。你可以使用 “+=” 来替代 “++”

spam = 0
spam += 1
print(spam)
1

13、错误地调用类中的方法

class Foo:
def method1():
print('m1')
def method2(self):
print("m2")

a = Foo()
a.method1()

报错:TypeError: method1() takes 0 positional arguments but 1 was given
说明:method1 是 Foo 类的一个成员方法,该方法不接受任何参数,调用 a.method1() 相当于调用 Foo.method1(a),但 method1 不接受任何参数,所以报错了。正确的调用方式应该是 Foo.method1()。

3.12 Python2.x与Python3.x版本的区别

3.12.1简介
Python3发布于2008年底,是一次重大的Python升级。Python3的有些改进不向Python2兼容,因此,Python2始终与Python3并行向前发展,不过,据Python官方说明,Python2.x将在2020年后停止维护,以后将以Python3.x为主。
3.12.2具体说明

print函数
在Python3中,print是函数式,而在Python2中,print是个语言结构,与if和for一样。以下几种情况,在Python2.7中是等价的。

print "ok"
print ("ok") #注意print后面有个空格
print("ok") #print()不能带有任何其它参数

对Python2.x中print函数,可以通过导入print_function ,即from __future__ import print_function,可以使Python2中print兼容Python3,如下例,在Python2中可运行如下语句:

from __future__ import print_function
print("fish", "panda", sep=',')

Unicode
Python 2 有 ASCII str() 类型,unicode() 是单独的,不是 byte 类型。
现在, 在 Python 3,我们最终有了 Unicode (utf-8) 字符串,以及一个字节类:byte 和 bytearrays。
由于 Python3.X 源码文件默认使用utf-8编码,这就使得以下代码是合法的。

中国='china'
print(中国)

在Python2.x中,需要添加u

a1="我喜欢Python"
a1
'\xe6\x88\x91\xe5\x96\x9c\xe6\xac\xa2Python'

a2=u"我喜欢Python"
a2
u'\u6211\u559c\u6b22Python'

在Python3.x

a1="我喜欢Python"
a1
'我喜欢Python'

除法运算
Python中的除法较其它语言显得非常高端,有套很复杂的规则。Python中的除法有两个运算符,/和//
首先来说/除法:
在python 2.x中/除法就跟我们熟悉的大多数语言,比如Java啊C啊差不多,整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数除法会保留小数点的部分得到一个浮点数的结果。
在python 3.x中/除法不再这么做了,对于整数之间的相除,结果也会是浮点数。
在Python2.x中

1/2
结果为
0
1.0/2.0
结果为:
0.5

xrange
在 Python 2 中 xrange() 创建迭代对象的用法是非常流行的。比如: for 循环或者是列表/集合/字典推导式。
这个表现十分像生成器(比如。"惰性求值")。但是这个 xrange-iterable 是无穷的,意味着你可以无限遍历。
由于它的惰性求值,如果你不得仅仅不遍历它一次,xrange() 函数 比 range() 更快(比如 for 循环)。尽管如此,对比迭代一次,不建议你重复迭代多次,因为生成器每次都从头开始。
在 Python 3 中,range() 是像 xrange() 那样实现以至于一个专门的 xrange() 函数都不再存在(在 Python 3 中 xrange() 会抛出命名异常)。
不等运算符
Python 2.x中不等于有两种写法 != 和 <>
Python 3.x中去掉了<>, 只有!=一种写法,还好,我从来没有使用<>的习惯。
数据类型
1)Py3.X去除了long类型,现在只有一种整型——int,但它的行为就像2.X版本的long
2)新增了bytes类型,对应于2.X版本的八位串,定义一个bytes字面量的方法如下:

b = b'china'
type(b)
str

str对象和unicode对象可以使用.encode() (str -> unicode) or .decode() (unicode -> str)方法相互转化。

3.13 练习

一、编写程序计算1+2+3+....+100的结果。
二、对字符串'12345678',将逆序输出。
三、创建一个10*10的随机数组并查找最大最小值

MySQL入门

第2章 MySQL入门

2.1 MySQL简介

2.1.1数据库泛述
为什么要学习数据库?
这个问题我觉得还是从反面来回答比较好,数据库出故障了,会发生啥呢?
学数据库与我找工作或找到更好工作有关系吗?有,关系还很大哦。当然,如果你去应聘不用电脑的职业除外。否则,很可能产生“一丑遮百俊”。
学了数据库有哪些好处?
其他好处不好说,但如果你学了熟悉数据库,对学习其他技术有非常大得帮助,尤其对学习大数据相关技术如Hive、HBase、SparkSQL、SparkRDD等等更是如此,数据库很多都是相通或相似的,学好一个学其他的就轻松多了。
有哪些数据库?
数据库种类很多,从大得方面来说,可分为关系型数据库和非关系型数据库,如MySQL、SQL Server、Oracle、DB2、Sybase等属于关系型数据库,近些年比较火的HBase、MongoDB、Redis等属于非关系型数据库,从存储方式方面来说,可分为行存储数据库、列存储数据库、键值数据库、NoSQL数据库等,当然各类关系型数据库或非关系型数据库自身都各有一些特点。这里就不展开说了。
如何学习数据库?
这个问题有点仁者见仁智者见智,一百人可能有一百个答案,不过我个人认为,数据库作为一个基础性非常强、使用非常广泛的系统,多花些时间进行一些有系统的学习是非常必要,好的基础将大大提升你的竞争力、拓展你的职业发展空间。
2.1.2 MySQL特点
为何选择MySQL? 它有哪些特点?
MySQL是由原MySQL AB公司自主研发的,现在已经被Sun公司收购,是目前IT行业最流行的开放源代码的数据库管理系统,同时它也是一个支持多线程高并发多用户的关系型数据库管理系统。支持Linux、Windows、MAC等多种操作系统,虽然功能上没有其他的大型数据库Oracle、DB2等那么齐全,但好用、易用、开源、可靠性等特点受到成千上万企业和用户的青睐重要原因。
向大家推荐几个学习MySQL的网站:
MySQL社区:http://www.mysqlpub.com/
MySQL菜鸟教程: http://www.runoob.com/mysql/mysql-tutorial.html
MySQL官网:http://www.mysql.com/
《深入浅出MySQL》唐汉名等著
2.1.3 数据库基础
数据库是一个长期存储在计算机内的、有组织、共享、统一的数据集合。作为关系型数据其关系可理解为数据库表,表是一系列二维数组的集合。
 表定义:表(Table),在关系型数据库中,表是一系列二维数组的集合,由纵向的列和横向的行组成,列又称为字段,行又称为记录。
表样例:stud_info(学生基本信息表)

 表要素有:
关系或表、列,字段行,记录
 数据类型:
字符串数据类型、整数型、日期/时间类型、浮点数据型等
 关键字,主键:
主键(Primary key),唯一标志表的每一条记录,可以定义表中一列或多列为主键, 主键列上不能有重复的值,也不能为空,如stud_info表中代码字段为该表的主键。
关系模式或表结构,格式为:表名称(属性1,属性2,…属性n)
2.1.4 数据库语言
我们一般通过数据库语言与数据库打交道,其中SQL是我们常用的,SQL的含义是结构化查询语言(Structured Query Language)。
SQL语言分类
 数据定义语言(DDL)
如:DROP,CREATE,ALTER等
 数据操作语言(DML)
如:INSERT,UPDATE,DELETE等
 数据查询语言(DQL)
SELECT等
 数据控制语言(DCL)
GRANT,REVOKE,COMMIT,ROLLBAK等。
为进一步理解SQL语句含义,下面以创建一张表的SQL语句为例,表名为t_student,具体SQL语句如下:

CREATE TABLE t_student
(
stud_id INT NOT NULL,
stud_name VARCHAR(40) NOT NULL,
stud_sex CHAR(1),
stud_birthday DATE,
PRIMARY KEY (stud_id)
);

这是一个典型的数据定义语言(DDL),该表共有4个字段,分别为stud_id,stud_name,stud_sex,stud_birthday,其中stud_id为主键。
目前这个表是空表,只有结构没有数据,我们可以用数据操作语句(DML)往表插入记录或数据。

INSERT INTO t_student(stud_id,stud_name,stud_sex,stud_birthday)
VALUES(1001001,'刘芳','F','1995-06-19');

以上记录是否成功插入到表中呢?我们可以通过数据查询语言(DQL)来验证一下:

select * from t_student;
+---------+-----------+----------+---------------+
| stud_id | stud_name | stud_sex | stud_birthday |
+---------+-----------+----------+---------------+
| 1001001 | 刘芳 | F | 1995-06-19

2.1.5 数据库系统架构


(图1-1 数据库系统架构)

2.1.6 Windows平台下安装配置
MySQL支持多平台,如常见的windows,linux等,这里我们以Windows下安装为主,然后简单说明在Linux平台上的安装。
这里介绍一个针对初学者的Windows版的MySQL安装程序(mysqlSetup.exe,文件下载链接:http://pan.baidu.com/s/1kVHK3eZ),文件大小为35M左右,版本为V5.0,依赖较少,安装方便,但基础功能都有。
以下为详细的安装步骤:
第1步:点击“mysqlSetup.exe”文件,弹出如下界面

第2步:选择安装类型,选择typical(典型安装)。

第3步:是否注册,选择skip sign-up(不注册)

第4步:开始配置服务器

第5步:在配置类型界面中,选择Detailed configuration(详细配置)

第6步:在服务器类型中,作为初学者,可以选择Developer Machine(作为开发机),占用系统资源较少,但基本功能都有。

第7步:字符集选择界面,第一个为西文编码,第二个是多字节的通用utf8编码,都不是我们通用的编码,这里选择第三个,然后在Character Set那里选择或填入“gbk”,当然也可以用“gb2312”,区别就是gbk的字库容量大,包括了gb2312的所有汉字。

第8步:在数据库用途界面,选择Mutifunctional Database(多功能数据库)。

第9步:进入服务器最多并发连接数界面,作为初学者,不涉及很复杂的业务逻辑,而且并发数也需要很多,可选择第一项OLAP或OLTP,并发连接数(concurrent connection)缺省为15,当然你也可以根据需要进行修改。

第10步:配置网络,使用缺省配置即可。

第11步:在Windows设置界面,记得在勾上“Inclode bin Directory in windows PATH”,这样自动把mysql命令所在目录放在Windows的环境变量Path中,接下来你便可在任何目录下启动mysql。


第12步,确认root用户密码

第13步,显示将执行的内容(无需选择),点击“Execute”

第14步安装结束

第15步登录MyQL系统。
 通过Windows命令行登录
第1步,从开始菜单选择“运行”,打开运行对话框,输入‘cmd’

第2步,按确定,打开DOS窗口。

第3步,在DOS窗口中,可以通过输入命令登录MySQL系统,命令格式为:
mysql -h hostname -u username -p
其中mysql为登录命令,
-h 后的参数为服务器名称或IP地址,如果为本地服务器,可为localhost 或127.0.0.1, -u后的参数为用户名称,还没有创建其他用户时,只有root用户。
-p 后的参数为用户密码。

第4步,退出mysql系统,只要在mysql>后,输入quit 然后回车即可。
 通过MySQL命令行登录
选择“开始”菜单,点击含“MySQL Command Line Client”字样的图标,进入登录界面,然后输入root用户密码即可登录。

这是在登录windows下MySQL系统的几种方式,下章我们将介绍如何登录远程服务器上的MySQL系统。

2.2 维护表结构

在关系型数据库中,表是是数据库中最重要、最基本的操作对象,是数据存储的基本单位,数据是按行存储的(非关系型数据库有些是按列存储的),同时通过表中的主键、外键、索引、非空等约束来保证数据的一致性和完整性。这一章主要介绍数据表的基本操作:如何创建表、查看表的结构、修改数据表、删除数据表等。
创建表的方式,可以命令行的方式,也可以通过客户端(navicat for mysql)或通过该客户端的建模方式创建,然后,把模型同步到数据库即可。
2.2.1 创建表
创建表的过程就是确定数据列的属性、制定数据完整性、一致性等约束的过程。而这些约束主要通过主键、外键、是否可空、索引等约束来实现的。
创建表的语法形式:
CREATE TABLE 表名称
(
字段名称1 数据类型 [列级的约束] [默认值],
字段名称2 数据类型 [列级的约束] [默认值],
--------
[表级的约束]
);
注:
(1)、表的名称不区分大小写,不能以SQL中的一些关键字为表名,如CREATE、ALTER、INSERT等;
(2)、列名间用逗号隔开。
下面以创建一个学生的基本信息表为例,说明如何创建一张表。
表的定义或结构如下:
学生信息(t_stud_info)表结构

注:
(1)、stud_code这个字段为主键,作为主键(PRIMARY KEY)的字段不能为空,为空的字段不能为主键,主键可以建的一个字段上,也可建的两个或两个以上的字段上,称为组合主键;
(2)、非空字段,是指这些字段的值,不能为空(即为null)。
创建表的SQL语句:
CREATE TABLE t_stud_info
(
stud_code varchar(20) NOT NULL,
stud_name varchar(100) NOT NULL,
stud_gend varchar(10) ,#没有说明NULL,说明是NULL
college_name varchar(300) NULL,
PRIMARY KEY (stud_code) #指明构成主键的字段
);#最后是分号
创建表的SQL以写好,如何执行,如何查看表结构?通过实例来说明:
 直接把创建表的SQL语句放在mysql命令行执行:

mysql> use testdb; ###把表创建在该数据库上
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> CREATE TABLE t_stud_info
-> (
-> stud_code varchar(20) NOT NULL,
-> stud_name varchar(100) NOT NULL,
-> stud_gend varchar(10) ,
-> college_name varchar(300) NULL,
-> PRIMARY KEY (stud_code)
-> );
Query OK, 0 rows affected (0.31 sec)
mysql> show tables; ###查看已创建的表
+------------------+
| Tables_in_testdb |
+------------------+
| stud_info |
| stud_info_copy |
| stud_score |
| stud_score_view |
| t_stud_info | ###表创建成功
+------------------+
5 rows in set (0.00 sec)

mysql> desc t_stud_info; ###查看表结构
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| stud_code | varchar(20) | NO | PRI | NULL | |
| stud_name | varchar(100) | NO | | NULL | |
| stud_gend | varchar(10) | YES | | NULL | |
| college_name | varchar(300) | YES | | NULL | |
+--------------+--------------+------+-----+---------+-------+
4 rows in set (0.17 sec)
mysql> show create table t_stud_info \G ###查看表的详细信息
*************************** 1. row ***************************
Table: t_stud_info
Create Table: CREATE TABLE <code>t_stud_info</code> (
<code>stud_code</code> varchar(20) NOT NULL,
<code>stud_name</code> varchar(100) NOT NULL,
<code>stud_gend</code> varchar(10) DEFAULT NULL,
<code>college_name</code> varchar(300) DEFAULT NULL,
PRIMARY KEY (<code>stud_code</code>)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ###表的存储引擎、字符集。

 通过执行脚本创建表
如果一次要创建几十张表,把创建表的语句直接放在mysql命令行运行就不方便了,此时我们可以把创建这些表的SQL语句保存本地当前目录下,名为t_stud_info.sql的文件中,,具体内容请参看以下system cat t_stud_info.sql部分,然后用source命令在mysql命令下执行这个文件即可。这个source命令有点像shell命令。

mysql>system cat t_stud_info.sql; ###查看sql文件内容
DROP TABLE IF EXISTS t_stud_info;
CREATE TABLE t_stud_info
(
stud_code varchar(20) NOT NULL,
stud_name varchar(100) NOT NULL,
stud_gend varchar(10) ,
college_name varchar(300) NULL,
PRIMARY KEY (stud_code)
);
mysql> source t_stud_info.sql;
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| stud_info |
| stud_score |
| t_stud_info | ###表创建成功
+------------------+

除了以上两种方法外,还有其他方法,如通过客户端创建、通过模型来创建等等,这里就不展开来说了。
2.2.2 修改表结构
表创建好以后,有时间我们可能根据新的需求,需要修改字段类型、字段名称、添加字段、新建其它约束如索引、是否可空等。当表中无数据时做这些修改比较方便,如果表已有数据可能就需要慎重,否则可能导致修改失败,此时建议备份原表数据,然后清空数据,再做修改,修改后根据新的规则把数据导入新表中。但添加字段、放大自动长度等与是否有数据无关。
2,2.2.1 修改字段类型
如果发现创建的表的某个字段长度太小,需要放大其长度,该如何修改呢?我们可以使用ALTER TABLE 语句来修改。
【注意】如果是正式环境的数据,记得先备份,后修改,有备无患。
修改表字段类型的语法格式:
ALTER TABLE <表名> MODIFY <字段名><字符类型>;
我们创建一张表test01 (a1 varchar(20),a2 int,a3 date),然后,修改a1字段的数据类型,由varchar(20)改为varchar(60)。具体操作如下:

mysql> CREATE TABLE test01 ###创建表test01
-> (
-> a1 varchar(20),
-> a2 int,
-> a3 date
-> );
Query OK, 0 rows affected (0.07 sec)

mysql> DESC test01; ###查看表结构
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a1 | varchar(20) | YES | | NULL | |
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)

mysql> ALTER TABLE test01 MODIFY a1 varchar(60); ###修改字段a1类型
Query OK, 0 rows affected (0.24 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> DESC test01;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a1 | varchar(60) | YES | | NULL | | ###修改成功
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

大家考虑一下,是否可以把INT修改为字符型,或把字符型修改整数型?如果要修改需要满足哪些条件?
除了可以修改字段类型,我们还可以修改表名称、字段名称、字段属性等。
2.2.2.2 修改字段名称
修改字段名称的语句与修改字段类型的不一样,其语法格式为:
ALTER TABLE <表名> CHANGE <旧字段名><新字段名><数据类型>;
现在我们把test01表中a1字段名称改为name,数据类型不变。

mysql> desc test01; ###查看表结构
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a1 | varchar(60) | YES | | NULL | |
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
mysql> ALTER TABLE test01 CHANGE a1 name varchar(60); ###把字段a1改为name
Query OK, 0 rows affected (0.38 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc test01;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name | varchar(60) | YES | | NULL | | ###修改成功
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

2.2.2.3 新增字段
表创建后,根据需要我们可以修改表名、修改表中字段名称、字段类型,当然也可添加字段,而且可以更加指定位置的添加,如果没有指定,缺省是添加到最后。添加字段的语法格式为:
ALTER TABLE <表名> ADD <新增字段名><字段类型> [字段约束条件][FIRST|AFTER 已有字段名];
我们还是以test01表为例,在name字段后,添加一个名为code的字段,数据类型为varchar(20),并且不能为空或not null。

mysql> ALTER TABLE test01 ADD code varchar(20) not null AFTER name;
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc test01;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name | varchar(60) | YES | | NULL | |
| code | varchar(20) | NO | | NULL | |
| a2 | int(11) | YES | | NULL | |
| a3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

2.2.3 修改表的字符集
表的字符集或缺省字符集也能修改?能,这应该也是MySQL特色之一吧,一般数据库系统字符集与数据库绑定在一起,但MySQL把字符集粒度精确到了表甚至字段。虽然这个功能很强大,但也存在很大风险,特别是表中有数据时,可能导致字符类型不兼容问题,为降低风险,还是这句老话,先备份,后修改。
这里的修改一般指修改表的缺省字符集,常用的字符集有:UTF8、GBK、GB2312、latin1等,其中UTF-8用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码。UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大。
GBK、GB2312等与UTF8不兼容,需要通过Unicode编码才能相互转换:
GBK、GB2312--Unicode--UTF8
UTF8--Unicode--GBK、GB2312 。
字符集涉及面比较广,如服务器或数据库或表字符集、应用字符集(如连接字符集、文件字符集等)、客户端字符,一般这些环节的字符集需要一致或兼容,尤其对中文而言,否则可能导致乱码。如何解决乱码问题,后面我们也有介绍。
以下是修改表的字符集的一个简单实例:

mysql>show create table test01 ; ###查看表存储引擎、字符集等信息
+--------+---------------------------------------------------------------------| Table | Create Table
+--------+---------------------------------------------------------------------| test01 | CREATE TABLE <code>test01</code> (
<code>name</code> varchar(60) DEFAULT NULL,
<code>code</code> varchar(20) NOT NULL,
<code>a2</code> int(11) DEFAULT NULL,
<code>a3</code> date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 | ###缺省字符集为utf8
----------------------------------+
mysql>ALTER TABLE test01 CONVERT TO CHARACTER SET gbk; ###修改缺省字符集为gbk
Query OK, 0 rows affected (0.12 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> show create table test01 ; ###检查修改是否成功
+--------+---------------------------------------------------------------------| Table | Create Table
+--------+---------------------------------------------------------------------| test01 | CREATE TABLE <code>test01</code> (
<code>name</code> varchar(60) DEFAULT NULL,
<code>code</code> varchar(20) NOT NULL,
<code>a2</code> int(11) DEFAULT NULL,
<code>a3</code> date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk | ###修改成功

修改方式或命令还有很多,大家可以借助其强大的help进一步获取其他命令的使用方法,如可以通过help ALTER TABLE查询其他使用方法,这里就不在一一例举了。
2.2.4 插入数据
往表中插入数据,有多种方法,如通过SQL语句、客户端、数据备份工具等。
1、通过SQL命令:insert into table_name (列1,列2,..) Values(‘’,’’,’’,….);
用这种方法需要注意列与值的对应关系;如果不指定列名,则指所有列:
如insert into table_name values(‘’,’’,’’,…),values(),
2、通过客户端导入;
3、利用工具(load data file或mysqlimport等)借助数据文件来导入数据。
这里我们介绍第1种方法,其它2种后面讲数据备份时将介绍。
往表test01插入一条记录。

mysql> select * from test01; ###查看表记录信息
Empty set (0.01 sec)

mysql> INSERT INTO test01(name,code,a2,a3)
-> VALUES('mysql','001',10,'2016-10-30'); ###插入1条记录
Query OK, 1 row affected (0.07 sec)

mysql> select * from test01;
+-------+------+------+------------+
| name | code | a2 | a3 |
+-------+------+------+------------+
| mysql | 001 | 10 | 2016-10-30 | ###插入成功
+-------+------+------+------------+

这是往插入1条记录,如果想一次往表插入多条记录如何实现呢?我们只要在后面添加个values即可,如同时往表中插入2条记录:
insert into table_name values(‘’,’’,’’,…) ,(‘’,’’,’’,…)
注意记录间用逗号。
具体操作请看下例

mysql> INSERT INTO test01(name,code,a2,a3) ###同时插入2条记录
-> VALUES('linux','002',20,'2016-10-30'),
-> ('spark','003',30,'2016-10-30');
Query OK, 2 rows affected (0.06 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> select * from test01; ###检查结果,插入成功
+-------+------+------+------------+
| name | code | a2 | a3 |
+-------+------+------+------------+
| mysql | 001 | 10 | 2016-10-30 |
| linux | 002 | 20 | 2016-10-30 |
| spark | 003 | 30 | 2016-10-30 |
+-------+------+------+------------+

2.3 增删改数据

存储数据时用来查询分析用的,所以查询分析数据是平时重要任务,但数据库中数据它不会自动生成,需要我们去维护,当然大部分是系统自动维护,不需要手工去操作,不过维护程序还是需要写的,这章我们介绍如何维护数据库数据。这里我们主要介绍如何新增数据、如何修改数据、如何删除数据等。
2.3.1 插入数据
插入数据语句的语法:
INSERT INTO表名[(列名1,……列名n)] values(值1,…..值n);
这个SQL语句一次往表中插入1条记录,如果一次要插入多条记录是否可以呢?可以,而且很方便,插入多条语句为:
INSERT INTO表名[(列名1,……列名n)] VALUES(值1,…..值n), (值1,…..值n),..;
下面我们还是通过一些实例来进步说明如何操作。

mysql> CREATE TABLE test03 (id INT NOT NULL,name VARCHAR(20),birthday DATE);
Query OK, 0 rows affected (0.17 sec)

mysql> DESC TEST03; ###查看表结构
ERROR 1146 (42S02): Table 'testdb.TEST03' doesn't exist
mysql> DESC test03;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| name | varchar(20) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.04 sec)

mysql> INSERT INTO test03 VALUES(100,'
张华', '2000-01-01'); ##对所有字段插值
Query OK, 1 row affected (0.16 sec)

mysql> INSERT INTO test03(id,name) VALUES(200,'
刘婷'); ###选择字段插值
Query OK, 1 row affected (0.13 sec)

mysql> SELECT * FROM test03;
+-----+--------+------------+
| id | name | birthday |
+-----+--------+------------+
| 100 | 张华 | 2000-01-01 |
| 200 | 刘婷 | NULL |
+-----+--------+------------+
2 rows in set (0.00 sec)
mysql> INSERT INTO test03(id,name) VALUES(300,'
貂蝉'),(400,'吕布'); ##插入多条
Query OK, 2 rows affected (0.03 sec)
Records: 2 Duplicates: 0 Warnings: 0

2.3.2 修改数据
修改数据也是很常见的,不过修改数据前,记得备份数据。如何备份数据后面将介绍。
修改数据的一般语法:
UPDATE表名SET列名1=值1,….列名n=值n
[WHERE条件];
以下以修改id为200对应的name为例,假设发现id为200对应的name输错了,不是刘婷,而是刘涛。

mysql> UPDATE test03 SET name='刘涛' WHERE id=200; ###修改id对应name值
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> SELECT * FROM test03 WHERE id=200; ###查询验证结果
+-----+--------+----------+
| id | name | birthday |
+-----+--------+----------+
| 200 | 刘涛 | NULL | ###修改成功
+-----+--------+----------+
1 row in set (0.00 sec)

2.3.3 删除数据
删除数据时,我们可以选择删除几条,或满足某些条件的记录,当然也可以删除所有记录。在日常工作中,删除数据需要非常谨慎,务必养成一个良好习惯,先备份,后删除。对生产环境数据、或正式环境的数据,不建议物理删除,最好采用逻辑删除的方式(即修改对应记录的状态或有效时间等)。数据都有价值。
用DELETE删除数据的一般语法:
DELETE FROM表名 [WHERE条件];
DELETE 加上条件,就可以有条件删除记录;如果没有条件,将删除所有数据。删除所有数据也可用TRUNCATE命令(这种方式删除数据比较快),其命令格式为:
TRUNCATE [table] 表名;
使用DELETE FROM 表名或TRUNCATE [table] 表名命令删除全部记录时,有一种情况需要注意,如果一个表中有自增字段,这个自增字段将起始值恢复成1.如果你不想这样做的话,可以在DELETE语句中加上永真的WHERE,如WHERE 1或WHERE true。

mysql> SELECT * FROM test03;
+-----+--------+------------+
| id | name | birthday |
+-----+--------+------------+
| 100 | 张华 | 2000-01-01 |
| 200 | 刘涛 | NULL |
| 300 | 貂蝉 | NULL |
| 400 | 吕布 | NULL |
+-----+--------+------------+
4 rows in set (0.00 sec)

mysql> DELETE FROM test03 WHERE (id=300 or id=400);
Query OK, 2 rows affected (0.02 sec)

mysql> SELECT * FROM test03;
+-----+--------+------------+
| id | name | birthday |
+-----+--------+------------+
| 100 | 张华 | 2000-01-01 |
| 200 | 刘涛 | NULL |
+-----+--------+------------+
2 rows in set (0.00 sec)

mysql> DELETE FROM test03;
Query OK, 2 rows affected (0.01 sec)

mysql> SELECT * FROM test03;
Empty set (0.00 sec)

2.4 查询数据

数据库主要功能是存储数据,但存储数据不是最终目的,存储数据最终目的是为了展示和分析,如何分析展示数据库中数据,数据查询就是重要手段。MySQL提供了功能强大、又非常灵活、非常方便的语句实现这些操作。这一章将介绍使用SELECT语句实现简单查询、子查询、连接查询、分组查询及利用正则表达式查询等。
2.4.1 一般查询语句
最简单的是SELECT [列名]FROM [表名] WHERE [条件] 。然后你可以在后面加上像[LIMIT][ORDER BY][GROUP BY][HAVING]等。
[列名]: 可以多个字段(列间用逗号分隔),也可所以字段(一般用*表示所有字段)
[表名]: 可以是一个表名或视图名,也可以是多表或多视图(表间用逗号分隔)。
[条件]: 为可选项,如果选择该项,将限制行必须满足的查询条件。
[LIMIT]: 后跟[位置偏移量,] 行数(第1行的位置偏移量为0,第2行为1,以此类推。)
[ORDER BY]: 后跟字段,可一个或多个,根据这些字段进行分组。
[GROUP BY]: 后跟可一个或多个字段,根据这些字段进行排序,升序(ASC)或降序(DESC)。
其后也可跟WITH ROLLUP,增加一条合计记录。
[HAVING]: 一般与GROUP BY一起使用,用来显示满足条件的分组记录。

2.4.2 单表查询
单表查询就是从1张表中查询数据,后续将介绍多表查询。为查询表数据我们需要先做些准备工作。
2.4.2.1 准备工作
准备工作包括:1、定义表结构,创建表;2、查看分析数据文件;3.把数据导入到表中。
1.首先我们创建一个存储学生各科成绩的表(stud_score),表的定义如下:


(表6-1 学生成绩表 stud_score)
字段名称 字段描述 数据类型 主键 外键 非空 自增
stud_code 学生代码 VARCHAR(20) 是 否 是 否
sub_code 学科代码 VARCHAR(20) 是 否 是 否
sub_name 学科名称 VARCHAR(100) 否 否 否 否
sub_tech 学科老师代码 VARCHAR(20) 否 否 否 否
sub_score 学科成绩 SMALLINT(10) 否 否 否 否
stat_date 统计日期 DATE 否 否 否 否
转换为建表的SQL语句为:
DROP TABLE IF EXISTS stud_score;
CREATE TABLE stud_score (
stud_code varchar(20) NOT NULL,
sub_code varchar(20) NOT NULL,
sub_name varchar(100) default NULL,
sub_tech varchar(20) default NULL COMMENT '教师代码',
sub_score smallint(10) default NULL,
stat_date date default NULL,
PRIMARY KEY (stud_code,sub_code)
);
2、创建这个表以后,我们需要把一个包含该表数据的文件(在slave02节点的/tmp目录下,名称为stud_score.csv)导入该表,另该文件第1行为字段名称,需过滤掉。我们先操作,具体语法等我们后续会介绍。
查看该数据文件信息

hadoop@master:/tmp$ pwd
/tmp
hadoop@master:/tmp$ ls -l |grep 'stud*'
-rw-rw-r-- 1 feigu feigu 1508 Jul 6 15:47 stud_info.csv
-rw-rw-rw- 1 mysql mysql 7157 Jul 2 11:32 stud_score_0702.txt
-rw-rw-rw- 1 mysql mysql 7157 Jul 2 21:43 stud_score_0703.txt
-rw-rw-r-- 1 feigu feigu 4652 Jul 2 10:26 stud_score.csv
hadoop@master:/tmp$ head -2 stud_score.csv
stud_code,sub_code,sub_nmae,sub_tech,sub_score,stat_date
2015101000,10101,数学分析,,90,
hadoop@master:/tmp$ cat stud_score.csv |wc -l ###查看文件记录总数
122

3.把数据文件导入表中

mysql> DROP TABLE IF EXISTS stud_score; ###判断是否存在,存在则删除
Query OK, 0 rows affected (0.36 sec)

mysql> CREATE TABLE stud_score ( ###创建表
-> stud_code varchar(20) NOT NULL,
-> sub_code varchar(20) NOT NULL,
-> sub_name varchar(100) default NULL,
-> sub_tech varchar(20) default NULL COMMENT '教师代码',
-> sub_score smallint(10) default NULL,
-> stat_date date default NULL,
-> PRIMARY KEY (stud_code,sub_code)
-> );
Query OK, 0 rows affected (0.07 sec)
mysql> select * from stud_score; ###查看是否有数据
Empty set (0.00 sec)
mysql> load data infile '/tmp/stud_score.csv' into table stud_score fields terminated by "," ignore 1 lines; ###把数据导入到表中
Query OK, 121 rows affected, 121 warnings (0.25 sec)
Records: 121 Deleted: 0 Skipped: 0 Warnings: 121
mysql> select count(*) from stud_score; ###查看验证记录总数
+----------+
| count(*) |
+----------+
| 121 |
+----------+
1 row in set (0.07 sec)

2.4.2.2 查看指定行数据

mysql> SELECT * FROM stud_score where sub_name='高等代数'; ###查看指定学科
+------------+----------+--------------+----------+-----------+------------+
| stud_code | sub_code | sub_name | sub_tech | sub_score | stat_date |
+------------+----------+--------------+----------+-----------+------------+
| 2015101000 | 10102 | 高等代数 | | 88 | 0000-00-00 |
| 2015101001 | 10102 | 高等代数 | | 78 | 0000-00-00 |
| 2015101002 | 10102 | 高等代数 | | 97 | 0000-00-00 |
| 2015101003 | 10102 | 高等代数 | | 87 | 0000-00-00 |
| 2015101004 | 10102 | 高等代数 | | 77 | 0000-00-00 |
| 2015101005 | 10102 | 高等代数 | | 65 | 0000-00-00 |
| 2015101006 | 10102 | 高等代数 | | 68 | 0000-00-00 |
| 2015101007 | 10102 | 高等代数 | | 80 | 0000-00-00 |
| 2015101008 | 10102 | 高等代数 | | 96 | 0000-00-00 |
| 2015101009 | 10102 | 高等代数 | | 79 | 0000-00-00 |
| 2015101010 | 10102 | 高等代数 | | 52 | 0000-00-00 |
+------------+----------+--------------+----------+-----------+------------+
mysql> SELECT * FROM stud_score LIMIT 3; ###查看前3行
+------------+----------+--------------+----------+-----------+------------+
| stud_code | sub_code | sub_name | sub_tech | sub_score | stat_date |
+------------+----------+--------------+----------+-----------+------------+
| 2015101000 | 10101 | 数学分析 | | 90 | 0000-00-00 |
| 2015101000 | 10102 | 高等代数 | | 88 | 0000-00-00 |
| 2015101000 | 10103 | 大学物理 | | 67 | 0000-00-00 |
+------------+----------+--------------+----------+-----------+------------+
3 rows in set (0.00 sec)

mysql> SELECT * FROM stud_score LIMIT 2, 2; ###查看第3行开始后2行
+------------+----------+-----------------+----------+-----------+------------+
| stud_code | sub_code | sub_name | sub_tech | sub_score | stat_date |
+------------+----------+-----------------+----------+-----------+------------+
| 2015101000 | 10103 | 大学物理 | | 67 | 0000-00-00 |
| 2015101000 | 10104 | 计算机原理 | | 78 | 0000-00-00 |
+------------+----------+-----------------+----------+-----------+------------+
2 rows in set (0.00 sec)

2.4.2.3 模糊查询
利用LIKE 关键字可以进行模糊查询。下例查询学科代码以101开头的记录总数。

mysql> SELECT count(*) FROM stud_score WHERE sub_code LIKE '101%';
+----------+
| count(*) |
+----------+
| 55 |
+----------+
mysql>select * from stud_info where birthday REGEXP '^1998' ;
mysql> select * from stud_info where birthday REGEXP '10$' ;

2.4.2.4 分组查询
如果我们希望按学科来统计学生成绩、班级成绩该如何实现呢?这就涉及到分组统计问题,如果需要按学科统计,可以GROUP BY sub_code;然后取前3名学科,具体实现请看下例:

mysql> SELECT sub_code,SUM(sub_score) from stud_score GROUP BY sub_code;
+----------+----------------+
| sub_code | SUM(sub_score) |
+----------+----------------+
| 10101 | 863 |
| 10102 | 867 |
| 10103 | 830 |
| 10104 | 932 |
| 10105 | 857 |
| 20101 | 870 |
| 20102 | 892 |
| 20103 | 866 |
| 20104 | 864 |
| 20105 | 822 |
| 20106 | 843 |
+----------+----------------+
11 rows in set (0.00 sec)
mysql> SELECT sub_code,SUM(sub_score) AS sum_score from stud_score GROUP BY sub_code
-> ORDER BY sum_score DESC LIMIT 3; ###按学科总分排序,取前3。
+----------+-----------+
| sub_code | sum_score |
+----------+-----------+
| 10104 | 932 |
| 20102 | 892 |
| 20101 | 870 |
+----------+-----------+

2.4.3 多表查询
多表查询,需要连接2张或2张以上的表一起查询,连接有多种方式,如内连接(通常缺省连接)、外链接(又分为左连接,右连接)等。多表连接时,建议不宜一下连接很多表,尤其是数据比较大时,可以采用两两连接等方式。
要实现多表连接,还需创建一个存储学生基本信息的表(stud_info)并导入数据.
2.4.3.1 准备工作
创建一个学生基本信息的表(表定义如下),并把数据(一个数据文件)导入到表中。
准备步骤,1.定义并创建表,2,查看分析数据文件,3. 导入数据,并验证导入结果。
1.定义并创建表

mysql> CREATE TABLE stud_info (
-> stud_code varchar(20) NOT NULL,
-> stud_name varchar(100) NOT NULL,
-> stud_sex char(1) NOT NULL default 'M' COMMENT '性别',
-> birthday date default NULL,
-> log_date date default NULL,
-> orig_addr varchar(100) default NULL,
-> lev_date date default NULL,
-> college_code varchar(10) default NULL COMMENT '学院编码',
-> college_name varchar(100) default NULL,
-> state varchar(10) default NULL,
-> PRIMARY KEY (stud_code)
-> ) ;
Query OK, 0 rows affected (0.05 sec)

2.查看分析数据文件

hadoop@master:/tmp$ ls -l|grep 'stud_info*'
-rw-rw-r-- 1 feigu feigu 1508 Jul 6 15:47 stud_info.csv
hadoop@master:/tmp$ cat stud_info.csv|wc -l ###查看文件记录总数
23
hadoop@master:/tmp$ head -3 stud_info.csv ###查看前3行
stud_code,stud_name,stud_gend,birthday,log_date,orig_addr,lev_date,college_code,college_name,state
2015101000,王进,M,1997/8/1,2014/9/1,苏州,,10,理学院,1
2015101001,刘海,M,1997/9/29,2014/9/1,上海,,10,理学院,1

3.导入数据并验证结果

mysql> select count(*) from stud_info; ###查看表数据
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql> load data infile '/tmp/stud_info.csv' into table stud_info fields terminated by "," ignore 1 lines;
Query OK, 22 rows affected, 22 warnings (0.05 sec)
Records: 22 Deleted: 0 Skipped: 0 Warnings: 22
mysql> select count(*) from stud_info; ###成功导入22条记录
+----------+
| count(*) |
+----------+
| 22 |
+----------+
1 row in set (0.00 sec)

2.4.3.2 多表连接查询
两表内连接、左连接,右连接的含义及使用关键字请参看下图:


(图6-1 多表连接示意图)

以下通过几个实例进一步说明多表连接的使用方法:

mysql> SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a,stud_score b WHERE a.stud_code=b.stud_code LIMIT 3; ###内连接,a定义为stud_info表的别名。
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
mysql> SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a LEFT OUTER JOIN stud_score b ON a.stud_code=b.stud_code LIMIT 3; ###左连接
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
3 rows in set (0.00 sec)
mysql> SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a RIGHT OUTER JOIN stud_score b ON a.stud_code=b.stud_code LIMIT 3; ###右连接
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
3 rows in set (0.00 sec)

2.4.3.3 子连接查询
这里介绍以用IN关键字实现子链接,基本格式为:
SELECT * FROM 表名WHERE 字段 IN (SELECT 字段 FROM 表名 WHERE 条件);
使用子查询,比较灵活,且有利于把大表关联转换为小表关联。下例为表stud_info中
stud_code从表stud_score中成绩为大于或等于90分的子查询中获取。

mysql> SELECT stud_name FROM stud_info WHERE stud_code IN(SELECT stud_code FROM stud_score WHERE sub_score>=90 ) LIMIT 3;
+-----------+
| stud_name |
+-----------+
| 王进 |
| 刘海 |
| 张飞 |
+-----------+
3 rows in set (0.02 sec)

2.4.3.4 视图查询

上面的查询语句,有的比较简单,有的比较复杂,像对那些复杂的查询语句,包含了很多信息量,而且有可能还要经常使用,但命令行是无法保证这些语句的,如果下次还要使用是否又重新写一遍呢?大可不必,我们可以把这个查询语句以视图的形式保存到数据库中,然后直接查询这个视图即可。
如上面内连接的SQL语句:SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a,stud_score b WHERE a.stud_code=b.stud_code LIMIT 3;
我们可以把它定义为一个视图V_STUD,然后查询视图即可,非常方便!而且视图会保存到数据库中,但视图本身不保存数据。具体实现请看下例:

mysql> CREATE VIEW V_STUD AS SELECT a.stud_name,b.sub_name,b.sub_score FROM stud_info a,stud_score b WHERE a.stud_code=b.stud_code LIMIT 3; ###创建视图
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM V_STUD; ###查询视图
+-----------+--------------+-----------+
| stud_name | sub_name | sub_score |
+-----------+--------------+-----------+
| 王进 | 数学分析 | 90 |
| 王进 | 高等代数 | 88 |
| 王进 | 大学物理 | 67 |
+-----------+--------------+-----------+
3 rows in set (0.00 sec)

2.5课后练习

一、在testdb库上创建表,表结构如下:

写一个sql文件,功能包括:
1、创建以上表结构,表名为:test_学号;
2、往表test_序号插入至少3条数据;
3、在表最后位置添加一个字段,字段要求如下:

二、创建表并把数据导入表中,然后查询相关数据,把这些操作放到一个SQL文件中,具体要求如下:
1、创建两表,表名分别为,stud_score_学号:stud_info_学号,表结构请参考第6.2.1节和6.3.1节
2、导入数据,把/tmp目录下stud_info.csv,stud_score.csv分别导入到表stud_info_学号,stud_score_学号中
3、查询数据,查询表stud_info_学号中,把姓刘的同学过滤出来。
三、多表查询,在一个SQL语句实现以下功能
1、内连接,实现stud_score_学号:stud_info_学号的内连接,连接字段为stud_code
2、左连接, 实现stud_score_学号:stud_info_学号的左连接,连接字段为stud_code
四、聚合查询
1、把选了大于5门课的同学的名称及各科总成绩打印出来。
五、写一个存储过程,实现功能,输入学院代码,输出,学生名称、学院名称、各科平均成绩,如下图:

Linux入门

第1章 Linux入门

1.1 Linux能做啥

Windows能做啥? 可能十个人十个都知道。具体能做啥,就不用我说了。Linux能做啥?为啥有了Windows,为啥还要Linux?
Windows与Linux的关系,打个不很恰当的比喻,如果把Windows和Linux都作为交通工具,Windows就想单车(自行车),Linux就像列车。前者方便易用,并几乎没有轨道的限制,但它只能单用户,能承载的重量有限;后者虽然不像前者方便易用,但它可以多用户,并且可以运送巨无霸的东西!如管理成千上万个节点的大数据集群,还有关键一点是安全,还免费,更不用说其庞大的志愿者和使用者,当然,它也有一些不足或不如人意的地方,如需通过命令方式操作,这给初学者带来不少挑战,不过Linux有很多奇妙的输入方法,使用这些方法将极大提高输入效率,这些方法我们后续章节将介绍,除了这些输入方式外,Linux还有很多更强大的方法,提高你的工作效率。

1.2 如何学习Linux

学好Linux将大大提升你的竞争力,哪如何学好它呢?是否存在捷径?我觉得有但又没有。
作为初学者,个人建议可以像小孩学英语一样,先学会用,而且是学哪些最常用、最基础、最重要的部分。如何会用,理解起来就方便多了。否则,已开发就学一大堆的语法,效果往往事倍功半。此外,就像锻炼身体、学习英语等一样,贵在实践再实践,“纸上得来终觉浅绝知此事要躬行”。
另,向大家推荐几个学习Linux的网站:
Linux中国:https://linux.cn
鸟哥的Linux私房菜: http://linux.vbird.org
Linux下载站: http://www.linuxdown.net
Linux公社: http://www.linuxidc.com

1.3 Linux的历史

Linux的前辈是Unix,但可能是由于历史原因,Unix派别林立而且主要用在大型服务器,如IBM的AIX,HP的HP-Unix,SUN公司(目前是Oracle的一部分)的SUN-Unix等等,更要命的一点是他们商业味道很浓。
1991年10月5日, 上午11时53分,有一个名为 Linus Torvalds 的年轻芬兰大学生在 comp.os.minix 这个新闻群组上发表了这样一个帖子,称他以bash,gcc等工具写了一个小小的内核程序,这个内核程序可以在Intel的386机器上运行,这引起很多的兴趣,由此标志L inux的诞生。
当初还是个大学生的 Linus 大概完全没想到当初被他视为个人兴趣的程式,在几年以后会有超千万个使用者,由他自己带头开发的操作系统现在已经在世界各地受到普遍的欢迎。

1.4 Linux的发行版本

Linux基于Debian、Red Hat和SUSE源的那些发行版本在生产环境中占据优势地位,以下为目前Linux比较流行的版本。

表1.1 Linux流行版本

对Linux而言,内核是其心脏,与硬件打交道,开机后留住在内存,而Shell为内核与应用程序间的桥梁,内核及shell构成Linux的主要内容,应用程序通过shell(或命令行)与服务器进行交互,shell调用内核(Kernel)来利用和管理服务器硬件资源,应用程序(如用户自己编写shell脚本等,office是windows操作系统的应用程序)保存在硬盘,需要时调入内存,它们之间的关系可用下图形表示:


(图1-1 Linux操作系统结构图)

1.5 登录系统

前面讲了Linux是个非常重要、功能强大、而且有意思的系统,而且还提到需要在命令行操作,不像使用windows一样,只需要按按鼠标即可,命令行操作确实不方便,尤其是对大多数用惯了windows的人来说。这确实有个‘痛苦’的过程,但是windows一般只能用来写写文档、上上网、打打游戏、看看电影之类,企业生产环境大都采用非windows环境,如Linux或Unix,因此对于大多数希望从事IT相关行业者,尤其进入大数据这个朝阳行业的人来说,是一道必需迈过的坎。
万事开头难,实际上你只要用过一段时间,将会慢慢喜欢上它,它有很多你想不到的优点,如tab补全功能。
如何登录Linux系统呢?如果Linux就在本地机器上,只需要启动Linux即可;如果Linu服务器不在本地,那也很简单,只要在你的客户端安装一个连接Linux的客户端(如Putty或Xshell等,这些在网上都可免费下载),然后做一个简单配置就可以了,下面我们以登录远程Linux服务为例(注:本例中Linux已安装好,并创建一个用户:feigu_shell):
第一步:安装Xshell 5
从网上下载一个Xshell 5,安装基本按照缺省情况即可
第二步:打开Xshell 5,点击文件菜单,然后点击新建,进入下一个界面

第三步:点击连接,配置服务器的IP,端口,如下图:

第四步:点击用户身份验证,配置用户及密码信息,如下图:

第五步:点击确认按键,所有配置完成,最后在会话界面将出现刚创建的连接,名为conn_linux,如下图。

第六步:点击连接,如果IP,用户密码等都正确的话,进入Linux系统,如能看到如下类似界面,祝贺你,你已经迈入Linux大门了!

Xshell 5是连接Linux服务器的工具之一,还可以通过Putty等工具,配置和使用也很方便,此外大家还可以利用Xftp 5工具来上传或下载Linux服务器上的文件,它的安装和配置比较简单,网上有很多这方面的文档,这里就不再展开来说了。

1.6 登录后,试试身手

登录Linux后,可以用一些简单命令,试一试Linux这个听起来有点神秘的操作系统。

注意,linux是区分大小写的或称为大小写敏感,所以pwd和PWD是不同的。
Linux系统要经常输入命令,而且有些命令或文件还比较长,如果要一个一个输入,不但麻烦,还容易出错。Linux是否有更有效的方法呢?答案是肯定的,那就是利用Tab键!它具有命令补全或文件补全的功能,这也是Linux很有趣的地方之一。当然,Linux为尽量减少你敲键盘次数,还有更强大的地方,如编写shell脚本,这个后面我们会详细介绍。
先看几个使用Tab键来偷点懒。
假如我们想进入homework1130这个目录,是否要cd homework1130?如采用Tab补全功能,在大大减少输入量的同时,还可大大提高你的准确率。具体实现方法为:输入目录名称的前几个字符即可,然后按Tab,系统自动补全该目录名称,具体操作如下图:


按Tab键后得如下界面

类似的神奇,Linux还有很多,如通过上下方向键(↑↓),可以调出前面输过的命令,输过了的命令可以不用重新输入了,这个功能也不错,顺便提一下,这些功能在MySQL数据库也有,讲MySQL时我们会介绍,看来好东西,大家都喜欢。

1.7 shell种类

shell是Linux内核与应用程序间的桥梁,起着非常重要的作用,shell由多种,如bsh(或sh),csh,ksh,bash等,这些shell基本内核对差不多,但还是有不少不一样的地方,所以我们在编写shell程序时,首先需要说明使用哪种shell来解释你程序,具体做法就是在shell程序第一行说明,如#!/bin/bash,这些内容大家先了解一下,后面我们会详细介绍,它们间关系大致如下:


(图1-2shell种类及关系图)

shell有多种,每个连接用户系统一般会先给个缺省shell(当然缺省shell可以修改),哪里可以看到你的缺省shell呢?查看文件/etc/passwd,或echo $SHELL 可以看到用户使用的缺省shell,查看/etc/shells可以看到目前已安装的shells。如何查看这些文件,后面将介绍。

1.8 文件与目录简介

说到文件和目录,我们从大家熟悉的windows开始,打开windows有关界面,我们通常能看到如下类似界面:


(图2-1 windows的目录及文件)

第[1]列为文件及目录名称:是文件还是目录,从图标很容易看出,其中有些为系统目录有 些是用户创建目录或文件。
第[2]列为修改时间;文件或目录的修改或创建时间。
第[3]列为类型:显式说了是文件夹还是文件,甚至为那种类型的文件。
第[4]列为文件大小,如果是文件,会显示其大小,单位为KB。
那Linux系统的目录及文件,又是如何排兵布阵的呢?windows进行下级目录,或上级目录只需要点击相关目录或到上级目录的方向图标,在Linux系统中如何进入下级目录或上级目录,如何打开文件?实际上在Linux系统虽然需要使用命令,但使用起来也很方便,而且在安全性、易用性方面更有独到的地方,目录的切换使用cd命令,查看当前目录或文件可以使用ls命令,详细操作我们如下实例。


(图2-2 Linux下的目录、文件及权限等)

用ls -la(,注意ls 与-la间有一个空格,l(list)及a(all)为两个参数)可以查看当前目录下的文件或目录的详细信息,包括列出隐含文件(文件名前为.,如上面的.bashrc文件)。
与windows的内容基本相同,windows的第[1]、[2]、[3]、[4]列分别对应Linux的第[7]、[6]、[1]、[5],虽然没有windows这么直观,但包含更丰富的信息。理解这些信息非常重要,以后经常提到这些相关内容,下面我们对上面7列代表的含义逐一进行说明。
 第[1]列:表示文件类型及权限,如果你仔细观察一下,会发现这列共有10个字符,
这10个字符的位置及含义,我们把它放大一下,如下图:


(图2-3文件及目录权限说明图)

第1字符为d或-,d表示对应是目录,-对应是文件;
第2,3,4字符,如[rw-]、[rwx],表示用户或文件所有者的权限,r(read)表示有读的权限,w(write)表示有写的权限,x(execute)表示有执行权限,-表示没有对应权限。[rw-]
表示文件所有者有读、写权限,但没有执行权限。
第5、6、7字符,表示文件所属的用户组的权限。
第8、9、10字符,表示其他用户(有点像windows中guest用户)的权限。

 第[2]列:表示文件或目录的连接数,如果目录至少为2,因任何一个目录至少包括.及..
两个目录,.表示当前目录,..表示上级目录,故如何我们需要切换到上级目录,可以cd ..
即可。
 第[3]列:表示文件或目录的所有者账号
 第[4]列:表示文件或目录所属用户组,如上面列出feigu feigu,说明所属用户为feigu,所属用户组也是feigu(这里两者同名,当然两者可以不同名)。
 第[5]列:表示文件大小,缺省单位为B(非KB)。
 第[6]列:表示文件文件或目录的创建或修改时间。
 第[7]列:表示文件或目录名称,如果文件前多一个点的表示隐含文件,隐含文件只有加上a这个参数才会显示出来,否则不会显示出来。
以上这些权限或属性当然不是一成不变的,可以通过命令修改,如何修改2.3节将介绍。

ls命令的使用方法还有很多,我这里介绍几种常用方法:
ls -lt ####结果按时间排序,降序。
ls -l ####结果按名称排序
ls -lS ####按文件大小排序,注意S为大写
ls -ltr ####结果按时间排序,变为升序,增加参数r(reverse),表示与缺省排序方式相反。
看到这里,可能有很多朋友不耐烦了,这么多命都要一一去记吗?其实完全不必要去死记忆,Linux有一个类似windows中help工具,他就是man这个命令,这个man非常强大,不过其来源是manual,而非男人哦。使用起来很方便,其格式为:man [command] ,显示结果中有各参数的含义、示例等。

1.9 切换目录

Linux的存储都是以目录的方式存在的,所以了解其目录结构、主要目录的含义非常重要,在不同目录间切换是Linux人员做得最多的事情之一,既然是经常干的活,是否有些妙招呢?有但又没有,关键还是要多练,“熟练才能生巧”。
我们先来看一看Linux的主要目录及用途:

 

(图2-4 Linux主要目录结构图)

切换目录使用cd命令,如果要到根目录(Linux系统最高目录),可以cd / 即可;如果切换到上级目录,使用cd ..;如果要回到用户主目录,用cd ~ 。
查看当前你所在的目录,使用pwd命令;查看当前目录下的有哪些文件或目录,使用ls命令。
.表示当前目录,也可用./表示;..表示上级目录,也可用../表示。
由根目录(/)开始的路径成为绝对路径,如:/home/feigu/hadoop;相对于当前路径的称为相对路径,如:./hadoop 、~/spark等。

1.10 创建文件或目录

前面我们介绍了Linux的目录结构、如何查看目录、如何修改文件或目录的属性和权限等内容,当然我们学Linux肯定不是来看看的,我们还想做一些实际事情,如创建文件或目录,拷贝文件,删除文件或目录等,这些任务在windows里相信大家都比较熟了,在Linux中该如何做呢?操作方便吗?不但方便还非常有趣,下面我们就来谈谈这方面的问题:
切换目录上面我们讲到了,利用cd命令即可,我们这里再小结一下:
cd /home/feigu/hadoop ####切换到指定目录
cd ~ ####切换到用户主目录,即用户一开始登录进入的目录
cd .. ####切换到上级目录
cd / ####切换到根目录
cd - ####切换到上一次使用的目录
查看目录可以用pwd,ls等命令。下面我们谈谈如何创建目录,用mkdir命令即可,其功能比windows前很多哦。其命令格式为:
mkdir [-p] 新建目录
-p ####可以递归创建目录,如果上级没有的系统将自动创建。示例如下:
 不用参数,创建单个目录

feigu@slave001:~/linux_test$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
feigu@slave001:~/linux_test$ mkdir mydirect ###创建新目录mydirect
feigu@slave001:~/linux_test$ ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect ###创建成功
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh

 使用-p参数,同时创建多级目录

feigu@slave001:~/linux_test$ ls -l
total 12
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
feigu@slave001:~/linux_test$ mkdir -p test1/test2 #同时创建两级目录
feigu@slave001:~/linux_test$ cd test1 #一级
feigu@slave001:~/linux_test/test1$ ls -l
total 4
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:26 test2 ###test1的子目录

删除目录的命令及其格式为:rmdir 目录名称
注意用rmdir命令删除目录时,需要该目录为空目录(即该目录下没有文件或子目录),否则,会报错。如果该目录下有很多文件和目录,岂不要逐一删除?是否有更强的命令?
有的,接下来这个命令就可。
rm [-fir] 文件或目录
-f ###f就是force之意,不提示,对不存的目录或文件也不报错或警告
-i ###i 就是interactive之意,删除前会询问是否删除
-r ###r就是recursive之意,即递归删除,这个非常强大,但也需非常谨慎使用。

feigu@slave001:~/linux_test$ ls -l
total 20
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
drwxrwxr-x 3 feigu feigu 4096 Oct 18 14:00 test1
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh
feigu@slave001:~/linux_test$ rm -rf test1 ###删除test1及其子目录test2
feigu@slave001:~/linux_test$ ls -l
total 16
-rw-rw-r-- 1 feigu feigu 154 Oct 16 11:11 conn_mysql.sh
drwxrwxr-x 2 feigu feigu 4096 Oct 18 11:35 mydirect
dr-xr--r-- 2 feigu feigu 4096 Oct 17 14:32 shell_script
-rwxr--r-x 1 feigu feigu 62 Oct 17 22:59 test.sh

与windows一样,对一些不满意的目录或文件,或根据实际情况,我们可重命名目录或文件名称,具体可使用mv命令,其格式为:
mv [-fiu] 原目录或文件目标目录或文件.
其实该命令也常用来移动目录或文件,就像windows中移动文件或目录一样。
目录或文件除了可以移动,当然可以复制,windows中有的,Linux也有。cp命令可以很方便用来复制文件或目录,其命令格式为:
cp [-fipr] 原文件或目录目标文件或目录

-f ###强制执行
-i ###如目标文件已存在,则会提示是否覆盖,覆盖按y,否则,按n
-p ###同时把文件或目录权限一起复制过去,而不仅仅默认权限或属性
-r ###递归复制,这个参数很给力
以上是一些常用参数,其实cp参数还有很多,大家可以通过man去了解更多参数的使用。
创建目录可以用mkdir,哪创建文件呢?创建文件比较简单,可用touch 文件名。当然也可用vi或vim等方法,vi或vim后续我们会讲到。

feigu@slave001:~/linux_test/mydirect$ ls -l
total 0
###用touch命令,创建一个名为myfile.txt的空文件
feigu@slave001:~/linux_test/mydirect$ touch myfile.txt
feigu@slave001:~/linux_test/mydirect$ ls -l
total 0
-rw-rw-r-- 1 feigu feigu 0 Oct 24 17:31 myfile.txt

【延伸思考】
如果要在两台服务器间复制文件,该如何操作呢?有兴趣可以考虑一下,没有兴趣的可跳过]

1.11 查看文件内容

前面我们谈了如何查看当前目录(pwd命令)、用ls命令查看当前目录下有哪些文件或目录,如何查看文件内容呢?在windows中我们要看一个文件需要打开才能看到,在Linux中,查看文件灵活的多,而且有趣的多。如可以不open也可查看,而且还可以顺着看,也可倒着看,等等,下面我们就来讲讲如何查看文件内容。
查看文件内容,我们可用的命令很多,常用的有:
cat ###从第一行开始查看,一次打开,如果文件较大时,建议采用其方法。
tac ###从最后一行开始看,tac啥意思?其实它没意思,就是cat的倒写。
more ###逐页地看,按回车,继续查看,按q退出。
less ###与more类似,但可以往前翻(按PgDn)。
###/ 字符串,可向下查询字符串
###?字符串, 可向上查看字符串
###n、N可继续查看以上字符串
###q退出。
head ###查看前几行
tail ###查看后几行
最后2个命令可指定查看头几行或最后几行,命令格式为:
head -n 数字文件名称 ####没有参数,缺省查看前10行
tail -n 数字文件名称 ####不带参数,缺省查看最后10行
以下为示例:

feigu@slave001:~$ head -n 5 stud_score_bak.csv ###查看前5行
stud_code,sub_code,sub_nmae,sub_tech,sub_score,stat_date
2015101000,10101,数学分析,,90,
2015101000,10102,高等代数,,88,
2015101000,10103,大学物理,,67,
2015101000,10104,计算机原理,,78,
feigu@slave001:~$ tail -n 3 stud_score_bak.csv ###查看最后3行
2015201010,20104,概率统计,,96,
2015201010,20105,汇编语言,,91,
2015201010,20106,数据结构,,87,

1.12 shell脚本

1.12.1 vim编辑器
vim编辑器是啥东西?它有哪些功能?为何要用vim?在windows中我们要编辑一个文件,可以用word、文本编辑器、excel等,如果要说这些编辑器哪个更像vim,那就是文本编辑器了,它们都不支持图片,但vim比文本编辑器功能上强很多,如支持语法高量、远程编辑、崩溃后文件恢复、与vi(vim是vi的加强版,vi也是很多Linux自带的编辑器)兼容等等,所以vim通常作为程序编辑器来使用。
刚开始用vi或vim来编辑文件或脚本时,大多数人都不习惯,这很正常,就像我们穿一双新鞋一样,大都有个磨合期。对于用惯word的朋友,在word中大部分动作基本用鼠标就可搞定,但在vi或vim中鼠标好像不起作用,Linux靠命令驱动,vi或vim也不例外,不过它有很多优点,时间久了你就可慢慢体会到。
好闲话少说,下面我们开始介绍vi或vim的使用方法,用vi或vim来编辑文件,操作上几乎一样,而且两者相互兼容,下面以vim为例来进行说明。
vim的使用分为三种模式,即普通模式、编辑模式、低行模式,了解这三种模式非常重要,实现这三种模式的转换很简单,就是按Esc键。
 普通模式
用vim打开文件时就进入了普通模式,在这种模式中,可使用方向键移动光标,也 可以删除、复制、粘贴字符
 编辑模式
顾名思义,在这种模式中,可以对插入、删除字符,在普通模式下,按下i或o或a之后,马上就进入到编辑模式,注意前提是在普通模式下。如果保证当前模式为普通模式,有个诀窍,多按几次Esc键。
 低行模式
在普通模式下,按“:或/或?”,就进入低行模式,为何叫低行模式?因此时光标会自动跳到低行,在低行模式下,可以进行保存、退出、查询等操作,这里同样要注意其前提,如何保证当前为普通模式,多按几次Esc键。
以下是vim三种模式的转换关系
(图3-1 vim三种模式间的转换图)

以下通过几个实例来说明:
(1)普通模式,利用vim或vi打开一个文件,处于普通模式

(2)在普通模式基础上,按i或a、o键,将进入编辑模式,在编辑模式下,用户可对文件进行增删改等操作。

(3)在编辑模式时按“Esc”键,将返回到普通模式,在普通模式时,按冒号(:),斜杠(/)或问号(?)将进入低行模式,低行模式可对文件进行保存退出等操作。:wq!表示保存并强制退出, :q! 不保存强制退出。

以上这些命令是最常用、最基础的,不过还有很多命令,而且还很多功能不错也很有趣的命令,大家可以从man或网上找到。
[注意:如果不正常退出或编辑时正好断网断线等异常情况,vim将在当前目录下创建一个扩展名为swp的暂时文件,利用这个文件可以用来恢复文件,恢复后可以删除该文件,否则下次再编辑该文件时,vim还会提示你是否要恢复或只读进入方式打开文件等。]
1.12.2 shell变量
说到变量相信大家都不陌生,像X,Y,Z都可以称为变量,与变量相对的就是常量,如a,b,c,2,3等等。shell的变量有不少特殊的地方,首先shell变量无需声明类型(顺便提一下:这点与Java、C、C++等不同,但与python 相似),下面具体讲讲shell变量的特点:
 变量的赋值:
格式为,变量名=变量值,
如,V1="abc"
字符串可以使用双引号"",和单引号'',但两者间有些区别,用双引号可以保留字符含 义,单引号将视特殊字符为一般字符,具体下面举例说明。
 变量的使用
需要在变量前加上$,如$v1 或${v1}。
 变量的显示:
echo $v1,下面通过一个示例说明双引号与单引号的区别:

feigu@slave001:~$ v1="numpy"
feigu@slave001:~$ v2="import $v1" ###双引号中含特殊字符$
feigu@slave001:~$ echo $v2
import numpy ###双引号保留了$的特殊含义
feigu@slave001:~$ v3='import $v2'
feigu@slave001:~$ echo $v3
import $v2 ###单引号内的$被作为一般字符

1.12.3 编写shell脚本
shell脚本(shell script)是啥样?为何要使用shell脚本?用了能带来啥好处?不用又会带来哪些不便?
shell脚本简单来说就是由shell命令写成一个程序,有点类似windows中dos下批处理文件(.bat),shell脚本无需编译就可直接运行。
假设哪天要你管理几十台甚至几百几千台Linux服务器,需要在每台服务器上创建很多相同目录、修改很多相同配置文件,你该如何处理呢?在每台服务器上都一个一个命令执行一下?恐怕几天都搞不完,即使完成了,也很难保证在每台服务器上做的都是一样。
如果我们把这些命令写成一个脚本,并在一台服务器上测试好,那么剩下的工作就是把这个脚本部署不同服务器上,运行一下即可(熟练的话,这些部署和运行都可一键搞定),如此不但快、而且质量也高。这就是shell脚本强大之一。
下面我们试着写一个简单的shell脚本,加深大家的理解。

feigu@slave001:~$ cat myfirst.sh ####shell脚本名称,以sh为扩展名
#!/bin/bash ###说明使用哪种shell解释你的程序,这里使用bash

#在界面上显示字符串 ###这一行为注释,shell注释用井号(#)
echo "I like linux!" ###把字符串“I like linux!”输出到窗口

关于编辑shell脚本的几个良好习惯
1、第一行说明用哪种shell解释你的脚本
如:#!/bin/bash
2、说明该脚本的功能、变更历史等。
3、添加必要的注释,方便别人更好理解你的程序,尤其是团体开发时。

脚本写好了,该如何执行呢?很简单哟,shell脚本无需编译,可以直接运行,运行方式大致有:
1、利用sh或bash运行,直接运行,但脚本权限要求不高,有读的权限即可。
如:sh myfirst.sh
2、利用. ./myfirst.sh或source ./myfirst.sh的方式,可直接运行, 有读的权限即可。
注./myfirst.sh前有空格
3、如果脚本有执行权限(即x权限),可采用如下格式:
./myfirst.sh

1.13 常用命令

在linux环境中,进行需要查找文件或目录,为此,我们可以利用find、或locate等命令;目前的大数据环境一般都是集群方式,如何访问集群中各个节点或服务器,如何在各节点间复制或传输文件等?这是本节主要介绍的内容。
 locate命令
locate也是从Linux的数据库文件中读取,但比whereis更灵活,其参数可使用正则表达式,其语法格式为:
locate [-ir] 文件或目录
-i ###查询时忽略大小写
-r ###后可接正则表达式

feigu@slave001:~$ locate -n 5 -i ipython ###查看含ipython目录或文件的前5条
/home/feigu/.ipython
/home/feigu/.ipython/README
/home/feigu/.ipython/extensions
/home/feigu/.ipython/nbextensions
/home/feigu/.ipython/profile_default
feigu@slave001:~$ locate -n 5 -r ^/var/lib/dpkg/info ###查看以/var/lib开头的
/var/lib/dpkg/info
/var/lib/dpkg/info/accountsservice.conffiles
/var/lib/dpkg/info/accountsservice.list
/var/lib/dpkg/info/accountsservice.md5sums
/var/lib/dpkg/info/accountsservice.postinst

 find命令
如果用whereis和locate无法查找到我们需要的文件时,可以使用find,但是find是在硬盘上遍历查找,因此将消耗较多资源,而且速度也比较慢,不过find可以根据指定路径或修改时间等进行查找,其语法格式为:
find [路径] [参数] [-print ] [-exec 或-ok command] {} \;
时间参数:
-mtime n ###列出n天之前的1天之内修改过的文件
-mtime +n ###列出n天之前(不含n天本身)修改过的文件
-mtime -n ###将列出n天之内(含n天本身)修改过的文件
名称参数:
-group name ###寻找群组名称为name的文件
-user name ###寻找用户者名称为name的文件
-name file ###寻找文件名为file的文件(可以使用通配符)
-print ###将查找到的文件输出到标准输出
-exec command {} \; ###将查到的文件执行command操作,{} 和 \;之间有空格
-ok ###和-exec相同,只不过在操作前要询用户
###列出1天内修改过的文件

feigu@slave001:~$ find /home/feigu/linux_test/ -mtime -1
###列出/home/feigu/linux_test目录下文件以myfirst开头的文件
feigu@slave001:~$ find /home/feigu/linux_test/ -name myfirst* -print

 操作集群:ssh或scp
如下图,假如我们需要通过一个客户端访问以下每台服务器,如何实现呢?我们可以在客户端安装xshell软件,然后通过ssh协议,访问集群中每台服务器,具体使用ssh命令,如何需要在服务器间copy文件,可以使用scp命令,这节介绍这些命令的具体使用。

hadoop@master:~$ ssh slave01 ###通过ssh命令,由master服务器登录到slave01
Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.13.0-61-generic x86_64)

* Documentation: https://help.ubuntu.com/
...................
Last login: Sun Mar 19 23:18:45 2017 from master
hadoop@slave01:~$ exit ###返回master服务器
logout
Connection to slave01 closed.
hadoop@master:~$ ls ##查看当前目录下的文件或目录
anaconda2 ch7.jar student_info.csv test.py
mydemo seafile testpbd.py
###以下命令为把master服务器上的文件test.py复制到slave01节点上
hadoop@master:~$ scp test.py hadoop@slave01:/home/hadoop

【延伸思考】
如何把一台服务器上的一个目录,复制到另一台服务器上?

1.14 课后练习

一、在登录用户当前目录下,创建一个名称为Linux01的目录,然后在该目录创建一个空文件test.txt
二、在第一题的基础上,往文件test.txt插入4行内容:
linux
python
hadoop
spark
三、列出几种查看test.txt文件内容的方式
四、编写一个shell脚本(名称可为:姓名全拼_所在学校.sh,如wufang_jiaoda.sh),该脚本的功能如:
1、在屏幕上显示你的大名;
2、在屏幕上显示当前服务器时间。
五、把wufang_jiaoda.sh复制到slave01节点上。

图像与TensorFlow

在使用卷积神经网络、循环神经网络等对图像进行深度学习时,一般需要对图像进行预处理,如进行格式转换,TensorFlow一般建议转换为TFRecord格式,它把二进制数据和标签数据存储在同一个文件中,这个格式文件不对数据进行压缩,可以快速加载到内存中。如何把图像转换为TFRecord格式?
在一般参考书上,通过使用TensorFlow自带的函数来生成,如input_data,这个函数一般只限定图像如,MNIST等。如果我们需要处理一般图像,将无法使用该函数。
这里我们介绍一种把图像转换为TFRecord格式的方法,该方法的一般步骤为:

对以上步骤进行详细说明:

1、获取数据
这里找我选了2类小狗的图片, Chihuahua和papillon, 全部 resize成128 * 128大小
保存地址为
/home/hadoop/data/dogs/n02085620-Chihuahua或n02086910-papillon
目录结构如下:

2、制作TFRecord文件
2.1 先讲一下tfrecord, 这是一种将图像数据和标签放在一起的二进制文件,能更好的利用内存,
为存储文件TFRecord
先创建目录: /home/hadoop/data/dogs/output
在tensorflow中快速的复制,移动,读取,存储 等等。
这里注意,tfrecord会根据你选择输入文件的类,自动给每一类打上同样的标签
如在本例中,只有0,1 两类。
2.2 以下是生成TFRecord详细代码:

import os
import tensorflow as tf
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np

filepath="/home/hadoop/data/dogs/"
classes={'n02085620-Chihuahua','n02086910-papillon'} #人为 设定 2 类
writer= tf.python_io.TFRecordWriter("/home/hadoop/data/dogs/output/dogs.tfrecords") #要生成的文件

for index,name in enumerate(classes):
class_path=filepath+name+'/'
for img_name in os.listdir(class_path):
img_path=class_path+img_name #每一个图片的地址

img=Image.open(img_path)
img= img.resize((128,128))
img_raw=img.tobytes()#将图片转化为二进制格式
example = tf.train.Example(features=tf.train.Features(feature={
"label": tf.train.Feature(int64_list=tf.train.Int64List(value=[index])),
'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
})) #example对象对label和image数据进行封装
writer.write(example.SerializeToString()) #序列化为字符串

writer.close()

          tf.Example 协议内存块包含了Features字段,通过feature将图片的二进制数据和label进行统一封装,然后将example协议内存块转化为字符串,  tf.python_io.TFRecordWriter 写入到TFRecords文件中。

 3.读取TFRecord文件

在制作完tfrecord文件后, 将该文件读入到数据流中。

def read_and_decode(filename): # 读入dogs.tfrecords
filename_queue = tf.train.string_input_producer([filename])#生成一个queue队列

reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue)#返回文件名和文件
features = tf.parse_single_example(serialized_example,
features={
'label': tf.FixedLenFeature([], tf.int64),
'img_raw' : tf.FixedLenFeature([], tf.string),
})#将image数据和label取出来

img = tf.decode_raw(features['img_raw'], tf.uint8)
img = tf.reshape(img, [128, 128, 3]) #reshape为128*128的3通道图片
img = tf.cast(img, tf.float32) * (1. / 255) - 0.5 #在流中抛出img张量
label = tf.cast(features['label'], tf.int32) #在流中抛出label张量
return img, label

4.显示tfrecord格式的图片

有些时候我们希望检查分类是否有误,或者在之后的网络训练过程中可以监视,输出图片,来观察分类等操作的结果,那么我们就可以session回话中,将tfrecord的图片从流中读取出来,再保存。

filename_queue = tf.train.string_input_producer(["/home/hadoop/data/dogs/output/dogs.tfrecords"]) #读入流中
reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue) #返回文件名和文件
features = tf.parse_single_example(serialized_example,
features={
'label': tf.FixedLenFeature([], tf.int64),
'img_raw' : tf.FixedLenFeature([], tf.string),
}) #取出包含image和label的feature对象
image = tf.decode_raw(features['img_raw'], tf.uint8)
image = tf.reshape(image, [128, 128, 3])
label = tf.cast(features['label'], tf.int32)
with tf.Session() as sess: #开始一个会话
#init_op = tf.initialize_all_variables()
init_op=tf.global_variables_initializer()
sess.run(init_op)
coord=tf.train.Coordinator()
threads= tf.train.start_queue_runners(coord=coord)
for i in range(20):
example, l = sess.run([image,label])#在会话中取出image和label
img=Image.fromarray(example, 'RGB')#这里Image是之前提到的
img.save(filepath+str(i)+'_''Label_'+str(l)+'.jpg')#存下图片
print(example, l)
coord.request_stop()
coord.join(threads)

代码运行完后, 将在目录"/home/hadoop/data/dogs"下生成带有标签名称一些文件,具体文件名称如下:

[feigu@webserver dogs]$ ll
total 132
-rwxr-xr-x 1 feigu feigu 4886 Jul 27 22:57 0_Label_0.jpg
-rwxr-xr-x 1 feigu feigu 4507 Jul 27 22:57 10_Label_1.jpg
-rwxr-xr-x 1 feigu feigu 3419 Jul 27 22:57 11_Label_1.jpg
-rwxr-xr-x 1 feigu feigu 4886 Jul 27 22:57 12_Label_0.jpg