Everyday sentence

2020/05/29 update:

This doc was originally established on 2020/04/28, now update to record my Internship. Mainly about a Smart City Platform and its relevant knowlege, especially in computer network and database.

Many thanks to Huang Junli, Bruce Wu and all the others group members. (FYI: xiaolin coding)

要求的能力

业务问题转化能力,数据敏感度,逻辑推理能力,推动策略落地,基本分析工具。通过数据采集获取数据,通过报表呈现获取信息(有背景的数据),通过挖掘分析获得知识(呈现出规律的信息),最后走向智能(自动完成任务)

我觉得比较重要的是分析方法论,实验设计分析,算法,沟通和spark/ hadoop等底层架构知识

增长黑客理论,确定当前增长重点,比传统方法更强调敏捷和实验。针对impact价值,confidence成功率,easy上线成本。所有策略必须通过上线实验评估收益

OSM模型:

Objective业务目标:业务目标是什么,用户诉求是什么,产品如何满足

Strategy实现策略:为了提升这一目标所采取的策略是什么

Measurement评估目标:追踪策略是否很好地满足了用户诉求

CSCE准则:

Comlete完备性:通过数据体系能够对产品的经营状况一目了然

Systemize系统性:数据体系可以粗略地定位到数据波动的原因

Comply可执行性:数据体系是量化并可实现的

Explain可解释性:容易被解释,容易被理解

从体验产品开始,深入思考再结合数据指标提出问题->敏捷分析->实验验证->优化产品,不断重复这个循环

Pandas的基础操作

pd.read_csv(‘xxx.csv’, names=new_columns, header=0)读取文件

pandas读取成的结构叫dataframe,他其中的每一列都叫一个series,操作类似于numpy中的矩阵,但是它还拥有行名和列名。

df.columns 可以查看所有列名。访问可以直接跟在点号后面或者以字典的方式访问。DataFrame(df, columns=['Change', 'Ratings'])可以取出某一些子列形成新的dataframe。赋值和修改可以用字典的方法,递交一个series列。如果用pd.Series(xxx, index = [ ]),index里还可以指定xxx的数值赋在第几个位置。

df.columns可以在直接传入列名的列表来实现批量修改,或者df.rename(columns={'a': 'A'},inplace=True)取修改列名。相应的index参数是行名。

df.sum()对列求和,sum(1)对行求和。df.apply( )对每个元素进行操作,里面可以设置一个lamda函数。或者直接df ** 2也等同于对元素操作。

列扩充直接进行赋值,行扩充用df.append( )里面放一个列名对应的键值字典,index参数指定行名。

df_b.join(df_a) 进行合并,如果索引不完全相同的话就只保留dfb的索引,缺失值用NaN填补。如果想合并并集就加上参数how=’outer’

清理无效数据

df[df.isnull()]   # 空值正常显示,非空值NAN
df[df.notnull()]  # 非空正常显示

df.dropna()     #将所有含有nan项的row删除
# 里面的axis参数0表示行,1表示列
# thresh表示删除小于其的行
df.dropna(how='ALL')       
# how表示筛选条件,All表示全都是NAN

df.fillna(0)   # 对NAN值填充0
# {1:0, 2:0.5} 字典参数指定第几列及填充何数
df.fillna(method='ffill')   
# method参数指示在列方向上以前一个值作为值赋给NaN

astype函数可以为某一行或者列进行数据类型转化

date_range函数用于生成固定频率的日期

Pandas理解

刚开始我也是疲于记忆各种语句和函数,而且不知道哪些功能有了函数,但是这既然是个数据结构,最好的理解方法还是去体会作者的思想。pandas的DataFrame相当于一个矩阵块拼起来的积木

.reset_index(drop=True, inplace=True)可以让index重新从零开始排列

用索引查询的时候先列后行,也可以先访问列属性,然后再index查询,因为pd表中的数据是按列存储的,一列就是一个pd.Series

.loc[ 行标签,列标签 ],里面还支持切片

.iloc[ 行索引,列索引],同样支持切片

.isin() 做判断,另外boolean也可以用于查询

s= pd.Series( value list, index = index_list ),df[“列名字”] = s 用来增加列

drop([ 标签名 ], axis = ) 可以做到0删除行,1删除列

Unicode和UTF-8的区别

他们都是字符流和字节流的转换方法。UTF-8还包含编码和解码的过程,在内存和资源的占用上会更小,但由于可变长的性质也不太好琢磨,所以utf-8编码在做网络传输和文件保存的时候,将unicode编码转换成utf-8编码;当从文件中读取数据到内存中的时候,将utf-8编码转换为unicode编码

Tornado Web

tornado是使用python编写的一个强大的,可拓展的web服务器,是一个编写对HTTP请求响应的框架

大多数社交网络应用都需要实时更新状态,但长连接和推送请求会使线程池饱和,tornado是一个异步的服务器,可以用于减轻基于线程池的服务器限制

当负载增加的时候,类似于Node.js, lighttpd, tornado这样的服务器使用协作的多任务方式进行优雅的拓展(挂起请求,等待资源就绪之后调用回调函数)

# 导入tornado的模块
import textwrap
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
# 从命令行中读取设置
from tornado.options import define, options

# 如果一个与define语句中同名的设置在命令行中给出,那么它将成为全局option的一个属性,如果使用了--help选项,就会打印文本。type是类型验证,不合适的时候会抛出一个异常
define("port", default=8000, help="run on the given port", type=int)

# 请求处理函数类,当处理一个请求时,Tornado将这个类实例化,并调用与HTTP请求方法所对应的方法 
class IndexHandler(tornado.web.RequestHandler):
    # 对HTTP的GET请求作出响应
    def get(self):
      # 查询字符串中取得参数greeting的值,如果没有就是第二个参数的作为默认值
        greeting = self.get_argument('greeting', 'Hello')
        # 写入HTTP响应
        self.write(greeting + ', friendly user!')

# 反转类
class ReverseHandler(tornado.web.RequestHandler):
    # 多了一个input,传进来的是匹配正则第一个括号里的内容
    def get(self, input):
        self.write(input[::-1])

# 处理不同路径的请求        
class WrapHandler(tornado.web.RequestHandler):
    # 用于接收HTTP的POST方法的请求
    def post(self):
        # 获取参数
        text = self.get_argument('text')
        width = self.get_argument('width', 40)
        # 用textwrap模块来制定样式写进服务器端的body里
        self.write(textwrap.fill(text, int(width)))

# 主函数入口
if __name__ == "__main__":
    # 解析命令行
    tornado.options.parse_command_line()
    # 创建Application实例,传递参数告诉tornado应该用哪个类来响应请求
    # 参数列表是一个元组列表,列表中的第一个元素是一个用于匹配的正则表达式,第二个是一个RequestHandler类
    # 正则表达式被看作已经包含行开始^和结束锚点$
    app = tornado.web.Application(
        handlers=[
            (r"/reverse/(\w+)", ReverseHandler),
            (r"/wrap", WrapHandler),
            (r"/", IndexHandler)
        ]
    )
    # 将其传递给HTTP server对象
    http_server = tornado.httpserver.HTTPServer(app)
    # 在命令行指定的端口进行监听
    http_server.listen(options.port)
    # 创建IOLoop实例
    tornado.ioloop.IOLoop.instance().start()

可以用curl在命令行窗口进行功能测试,get请求后面直接加URL,其中符号前面需要加转义符。post请求加-d 后接参数,最后放URL

运行结束后会返回HTTP状态码,如果出现错误想要自定义返回的东西,可以在RequestHandler类中重写write_error(self, status_code, **kwargs)的方法,方法内还是用write函数

GET 和 POST

在万维网世界中,TCP就像汽车,用TCP传输数据的时候很可靠,不会出现丢件少件的情况,但是如果路上的车是无差别的,又会引起混乱,所以HTTP充当的是交通规则的角色,HTTP分了几个服务类别(GET,POST,PUT,DELETE等)

当执行GET请求的时候,要给汽车贴上GET的标签,把传送的数据放在url里便于记录。处理POST请求的时候,贴上post标签,然后把货物放在body里

他们有一个重大区别: GET产生一个TCP数据包,POST产生两个TCP数据包。GET请求的时候,浏览器会把header和data一并发送出去,POST请求的时候浏览器先发送header,服务器响应100 continue,然后再发送data。但是有些浏览器会只发送一次,用GET代替POST优化网站性能

与前端的交互

在handler中的get,post函数里用render重定向到html文件,在主函数的app对象中用os.path模块设置template_path告诉模版文件所在的路径。)

html文件是模版文件,我们可以用Template和generate函数来试用,双花括号括起来的是占位符,占位符离除了可以放关键字参数,还可以放运算式,python表达式(用双%号包围逻辑语句),函数等

from tornado.template import Template
content = Template("<html><body><h1></h1></body></html>")
print(content.generate(header="Welcome!"))

输出<html><body><h1>Welcome!</h1></body></html>

安全和幂等

安全,指请求方法不会破坏服务器上的资源

幂等,意思是多次执行相等的操作,结果都是相同的

所以GET是安全且幂等的,POST都不是

POSTMAN

用来便捷测试web请求情况,新建request然后填入域名和方法类型,在body里用form-data的形式传入参数,可以得到网站的反馈。其他多不多说了,提几句有关身份验证的东西

一般分为四种,第一种是基础验证,直接把用户名和密码放在请求的header中;第二种Digest auth,复杂一些,生成authorization header;第三和第四种是OAuth 1.0,OAuth 2.0,不清楚

HTTP

超文本传输协议,超文本是指除了普通的文本之外,还包含图片,视频,超链接,常见的比如Html。所以HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。

常见的字段:

host:客户端发送请求时,用来指定域名,可以访问同一服务器上的不同网站

Content-Length:表明本次回应的数据长度

Connection:客户端要求服务器使用 TCP 持久连接,以便其他请求复用。HTTP/1.1 版本的默认连接都是持久连接,但为了兼容老版本的 HTTP,需要指定 Connection 首部字段的值为 Keep-Alive

Content-Type:服务器回应的时候告诉客户端本次数据是什么格式

Accept:客户端请求的时候,用Accept字段声明自己接受哪些格式

Content-Encoding:数据的压缩方法,表示服务器返回的数据使用了什么压缩格式

Accept-Encoding:说明自己可以接受哪些压缩方法

HTTP特性

简单,灵活,易于拓展,应用广泛,跨平台

简单:报文格式就是header + body,头部信息也是key-value的格式

灵活和易于拓展:各类方法允许自定义和扩充,工作在OSI第七层,HTTPS在HTTP和TCP层之间增加了SSL/TLS安全传输层,HTTP/3把TCP层换成了基于UDP的QUIC

无状态,明文传输,不安全

无状态:在进行有关联性的操作,如登录->添加购物车->下单->结算->支付的时候,每一次都要问一遍身份信息。可以用Cookie解决,这是一种通过在请求和响应报文中写入Cookie信息来控制客户端状态的方法

明文传输:F12或者Wireshark抓包可以肉眼查看,为我们的调试工作带来便利,但是也很容易被窃取

不安全:不验证通信方的身份,可能遭遇伪装。无法验证报文的完整性,所以有可能被篡改

HTTP/1.1,HTTP/2,HTTP/3

HTTP/1.1相比于1.0

长连接:为了改进每发起一个请求都要建立一次TCP连接的问题,持久连接减少了重复建立和断开的额外开销,减轻了服务器端的负载

管道网络传输:一个请求发出去以后不用等其回来就可以发送第二个请求,可以减少整体的响应时间

HTTP2相比于1.1

头部压缩:如果发出多个请求的头是一样或者类似的,协议会消除重复的部分(HPACK算法,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不再发送同样的字段了,只发送索引号)

二进制格式:放弃纯文本形式的报文,采用二进制格式,头信息和数据体都是二进制,并且统称为帧

数据流:每个请求或者回应的所有数据包被称为一个数据流,每个数据流都有一个独一无二的编号,客户端发出的数据流编号为奇数,服务端发送的编号为偶数。客户端还可以指定数据流的优先级,优先级高的请求,服务器就先响应

多路复用:可以在一个连接中并发多个请求或回应,而不用按照顺序一一对应,也就是说响应可能不是顺序的。这样不需要排队等待,消除了队头堵塞问题,降低了延迟,大幅度提高了连接的利用率

服务器推送:浏览器刚请求HTML的时候,服务器就提前把可能会用到的JS,CSS文件等静态资源主动发送给客户端,减少等待的时间

HTTP3相比于2

2的主要问题在于多个HTTP请求在复用一个TCP连接,如果一旦发生了丢包,TCP重传机制,这样在一个TCP连接中所有的HTTP请求都必须等这个丢了的包被重传回来

3把TCP协议层改成了UDP,为了改进UDP的不可靠性,使用了QUIC协议

QUIC是一个在UDP之上的伪TCP+TLS+HTTP/2的多路复用协议,有自己的一套机制可以保证传输的可靠性,当某个流发生丢包的时候,只会阻塞这个流,其他流不会收到影响

TLS升级成了1.3版本,头部压缩算法也升级为Qpack

QUIC把之前TCP和TLS/1.3的6次交互合并成了3次

重提HTTP和HTTPS

中间加入了SSL/TLS安全协议,使得在三次握手之后还要进行SSL/TLS握手过程,才可以进入加密报文传输

HTTP端口号是80,HTTPS端口号是443

针对HTTP的窃听,篡改,冒充风险,采用了信息加密,校验机制和身份证书来应对

安全是如何实现的

混合加密,摘要算法,数字证书

混合加密: 公开密钥加密方式处理起来比共享密钥加密要慢,但比共享密钥安全。所以HTTPS在通信建立前采用非对称加密的方式交换会话密钥,在通信过程中采用对称加密的会话密钥加密明文数据

摘要算法:客户端在发送明文之前会通过摘要算法算出明文的指纹,发送的时候把指纹+明文一同加密成密文之后再发送给服务器,服务器解密后,用相同的摘要算法对明文进行指纹计算,比较携带的指纹,若相同就是完整的

数字证书:客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥进行解密。数字证书是借助了第三方权威机构CA,将公钥放在数字证书里,只要证书是可信的,公钥就是可信的

SSL/TLS 协议基本流程:

建立的握手阶段描述(1.2四次握手,1.3优化为三次):

ClientHello请求:客户端向服务器发送客户端支持的SSL/TLS协议版本,客户端生产的随机数(后续用于生产会话密钥)和客户端支持的密码套件(如RSA加密算法)

SeverHello:服务器发送回应,确认协议版本,服务器生产随机数,用于后续生产会话密钥,确认密码套件,服务器的数字证书

客户端回应:客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的CA公钥,确认服务器的数字证书的真实性,取出服务器公钥加密报文,发送一个随机数,加密算法改变通知,客户端握手结束通知,同时把摘要算出来供服务端校验

服务器的最后回应:服务器收到客户端的第三个随机数之后,通过协商的加密算法,计算出本次的会话密钥,发送回应(加密通信算法改变通知,服务器握手结束通知,表示握手阶段结束,同时把之前所有内容发生的数据做个摘要,供客户端校验)

接下来就进入了加密通信,使用普通的HTTP协议,用会话密钥来加密内容

数据库连接

在python3上已经改用Pymysql了, 先connect上(关闭用close() ),然后创建一个cursor对象

在其上用execute(“sql”)方法执行查询语句

fetchone() 方法获取单条数据

fetchall() 方法获取所有数据的列表,每条数据被包装成列表里的一个元组

创建表:先DROP TABLE IF EXISTS Tablename,再创建表

表更改:用 try: cursor.execute(sql), db.commit() except: # 如果发生错误则回滚 db.rollback()

除此之外,还有一些其他类似的方法,比如psycopg2,这是用于连接postgreSQL的,用法也是先connect上得到conn,然后conn再创建一个cursor,执行excute。还有sqlalchemy的create_engine,用string中的Template方法在sql字符串中插入变量,再结合pd.read_sql_query和to_sql方法实现读取和更新

另外,在sqlalchemy中其实也可以绕过sql语句的部分,它作为一个ORM框架,可以把数据库表的一行记录与一个对象互相做自动转换,做法如下:

# 导入:
from sqlalchemy import Column, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 创建对象的基类:
Base = declarative_base()

# 定义User对象:
class User(Base):
    # 表的名字:
    __tablename__ = 'user'

    # 表的结构:
    id = Column(String(20), primary_key=True)
    name = Column(String(20))

# 初始化数据库连接:
engine = create_engine('mysql+mysqlconnector://root:password@localhost:3306/test')
# 创建DBSession类型:
DBSession = sessionmaker(bind=engine)
session = DBSession()
# session有add,commit, close方法
# session.query(User).filter(User.id=='5').one()

总结来说就是把表转化为类,把记录转化为对象,把字段转化为对象属性。不同的表对应不同的类,类间的关系可以通过relationship和ForeignKey来实现

Template方法

Template方法用()包裹文字,$符号记变量的名称,然后用substitute和safe_substitute方法传入字典进行替换,和print中的控制格式区别就在于不需要按序传参。safe方法在缺少参数的情况下也不会报错,而是会直接输出$ + 变量名

阶段性总结1:数据质量审查模块

连接数据库,主要用的是sqlalchemy里的engine。我们的原始表存储在postgre中,客户提供的要求表在mysql中,我们处理之后的表也会输出到mysql中去。

检查数据质量的时候碰到了一些问题,首先判断元素类型的时候可以用dtype函数进行查看。另外当索引序号不对齐的时候,或者说我们需要进行双条件查询的时候,我的程序中是先取出条件一的那一行,做法是用bool语句判断再取行,然后再用条件二取列,值得注意的是如果就这样取出的是一个object类型的块,我们要取元素需要再进行取行[ 0 ]。如果我们之前对数据已经进行过截取的话,我们为了保证index正确,可以对筛选后的结果进行index重排(.reset_index(drop=True, inplace=True))

Pyecharts的使用

Plotly Express

一个新的高级python可视化库,是plotly的高级封装,受Seaborn(matplotlib的升级)和ggplot2的启发,使用上更加简洁高效,不需要冗长的代码

Dash是Plotly的开源框架,用于构建图表的分析应用程序和仪表盘进行前端展示

阶段性总结2:数据融合模块

化工区有很多系统的数据信息,我们需要设计不同的场景需求,对数据进行融合,从而把主题相关的数据从不同表中融合到一起

pandas的merge函数可以做类似sql的join的事情,它按默认相同字段进行合并,并选取两个都有的。on参数指定合并字段,left_on, right_on参数指示不相同名字的字段,合并后会同时显示两个字段的列,所以需要删掉一行drop(‘xxx’,axis=1)。how参数定义连接方式,inner,outer,left,right。

pivot_table:

python的log模块进行日志记录

日志文件可以使我们知道程序运行的细节,如果用print语句的话,当程序写好运行的时候,我们要把他们删除,在程序比较复杂的时候,这个办法很低效。

import logging
# 在level中,我们可以通过改成logging.DEBUG来控制一些语句是否被输出
# filename参数控制输出日志的路径
# filemode参数指示直接写入还是追加写入
# format控制输出的格式 format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S'
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

logger.info('Start reading database')
# read database here
records = {'john': 55, 'tom': 66}
logger.debug('Records: %s', records)
logger.info('Updating records ...')
# update records here
logger.info('Finish updating records')

logging模块是自带的,debug模式用于调试,info模式查看程序是否如预料进行,warn模式是预料之外的,但不影响程序运行,error和critical是比较严重的问题。默认的level是warn,所以debug和info的信息就不会被输出到日志里了。

第三方库pysnooper

一个更加好上手的调试工具,@pysnooper.snoop()函数就可以实现对函数的调试,不需要繁琐的配置。里面可以加入监听结果重定向的路径,也可以加变量,函数或者线程参数

Padle飞桨

阶段性总结3:五个场景的合并

在pycharm中大家也许都会看到不同颜色的文件

灰色:已经同步的文件 红色:本地存在但是没有同步到仓库的文件。当添加到仓库时,此文件名变绿 蓝色:表示文件有更改且未同步到仓库中

别人修改了文件并且提交PUSH,你也修改了此文件但没有add和commit.然后你在pull时就会出现error

如果你想放弃你的修改,那么git reset –hard master

如果你不想放弃git stash贮存修改,然后git pull工作区恢复到最后一次提交的内容,git stash pop自动合并(但我实测好像也可以pull完以后commit + push)

date_range生成时间序列的时候格式必须是规范的年份日期格式

strptime可以实现timestamp到字符串的转化

Df.round()可以直接实现保留小数

safari抽风

选取“显示”>“文本编码”改成unicode就又可以快乐地玩耍啦

Wireshark的使用