Skip to main content

· 3 min read
info

在网络开发和服务器管理中,经常需要访问远程服务器上的服务或应用程序。 SSH端口转发(ssh -L)提供了一种方便的方式,可以将远程服务器的端口暴露到本机,使我们可以直接在本地使用这些服务

命令格式

ssh -L <本地端口>:<目标服务器地址>:<目标服务器端口> <SSH用户名>@<SSH服务器地址>

注意: <本地端口>必须是未被使用的,否则会出现错误。

  • <本地端口>:您希望在本地计算机上使用的端口号。
  • <目标服务器地址>:远程服务器的IP地址或主机名。
  • <目标服务器端口>:您希望访问的远程服务器上的端口号。
  • <SSH用户名>:远程服务器上的SSH用户名。
  • <SSH服务器地址>:远程服务器的IP地址或主机名。

检查端口是否可用

# Windows 检查端口是否可用
netstat -ano | findstr <端口号>
# Linux
netstat -ano | grep <端口号>

例如:我们可以把远程服务器的9000端口映射到本地的9000端口,这样就可以访问localhost:9000 跟访问远程服务一样 不用考虑为远程服务器去开放端口了

ssh -L 9000:localhost:9000 user@10.0.0.2

现在,您可以在本地计算机上使用localhost:9000来访问远程服务器上的服务或应用程序。 任何发送到本地计算机的9000端口的请求都将通过SSH通道转发到远程服务器上,并从远程服务器的9000端口返回响应。

优点

通过使用SSH端口转发,我们可以方便地在本地开发环境中使用远程服务器上的服务,而无需直接连接到远程服务器。这为开发人员和系统管理员提供了更加灵活和安全的远程访问方式

· 3 min read

什么是窗口函数

在 SQL 中,窗口函数是一种特殊的函数,它可以对一个窗口(一组行)进行计算,而不是对整个结果集进行计算。这种功能对于分析和聚合数据非常有用。

窗口函数的语法

在 MySQL 中,窗口函数的语法如下:

SELECT column1, column2, ..., function(column) OVER (PARTITION BY column1, column2, ... ORDER BY columnX DESC/ASC) AS result_column
FROM table_name;

其中,column1, column2, ... 是你要对其进行分组的列,columnX 是你要根据其排序的列,function(column) 是你要应用的窗口函数。

窗口函数的用途

使用窗口函数,你可以轻松地执行以下任务:

  • 计算行与组之间的排名
  • 计算行与组之间的百分比
  • 计算移动平均值或移动总和
  • 计算当前行与之前或之后的行之间的差异
  • 计算累积和或累积平均值等

下面是一些窗口函数的例子:

示例:

假设你有一个包含员工工资信息的表格。以下是一些可以使用窗口函数执行的任务和相应的查询示例。

  1. 为每个部门计算平均工资:
SELECT department, AVG(salary) OVER (PARTITION BY department) AS avg_salary
FROM employees;
  1. 计算每个部门的工资排名:
SELECT department, salary, DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
FROM employees;
  1. 计算每个部门的工资百分比:
SELECT department, salary, PERCENT_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_percentile
FROM employees;
  1. 计算每个员工的薪水与其部门平均薪水之间的差异:
SELECT name, department, salary, AVG(salary) OVER (PARTITION BY department) - salary AS salary_difference
FROM employees;

这些是使用窗口函数的一些示例,你可以使用类似的查询来执行各种任务。窗口函数是 SQL 中非常强大的工具,可以帮助你更轻松地对数据进行分析和聚合。

· One min read
info

netcat(也称为 nc)是一种用于读取和写入网络连接的工具。它可以用作一个简单的客户端,也可以用作一个简单的服务器。

安装

$ sudo apt-get install netcat

使用

这是一些常用的 netcat 用法:

在主机上创建一个 TCP 监听器:

$ nc -l <port number>

向远程主机发送数据:

$ echo "Hello, World!" | nc <remote host> <port number>

从远程主机读取数据:

$ nc <remote host> <port number>

在本地主机和远程主机之间创建一个 UDP 连接:

$ nc -u <remote host> <port number>

执行端口扫描:

$ nc -v <remote host> <port number>

这只是一些基本的 netcat 用法,您可以通过查看 man nc 来了解更多信息。

· One min read
info

Linux配置静态IP, 不同版本之间有不同的区别

Ubuntu 22.04 LTS

$ vim /etc/netplan/00-installer-config.yaml

# This is the network config written by 'subiquity'
network:
ethernets:
ens33:
dhcp4: false # close dhcp (set true is open)
addresses:
- 10.0.0.50/24 # set static ip
routes:
- to: default
via: 10.0.0.1 # set gateway
nameservers:
addresses: [8.8.8.8] # dns
version: 2

重启网络

$ sudo netplan apply

· 3 min read
info

GitHub Action 是 GitHub 推出的一个CI\CD服务, 可以实现自动构建与部署

使用 GitHub Action

示例:静态博客自动编译并部署

# 流水线名称
name: deploy

# main 分支接受到 push请求后触发流水线执行
on:
push:
branches:
- main

jobs:
deploy:
# 可配置流水线运行在什么版本的容器中
runs-on: ubuntu-latest
name: Build and Deploy

# 执行步骤
steps:
- name: Pull Code
uses: actions/checkout@v2
with:
ref: main

- name: Use nodejs
uses: actions/setup-node@v3
with:
node-version: '18.x'

- name: Use Cache
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

- name: Build
run: |
npm install
npm run build

- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
personal_token: ${{ secrets.ACCESS_TOKEN }}
publish_dir: ./build
publish_branch: gh-pages
full_commit_message: ${{ github.event.head_commit.message }}

执行步骤

  • 拉取指定分支代码
name: Pull Code
uses: actions/checkout@v2
with:
ref: main
  • 配置依赖版本,此处博客使用的 nodejs
name: Use nodejs
uses: actions/setup-node@v3
with:
node-version: '18.x'
name: Use Cache
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

例如,使用hashFiles函数可以在依赖关系改变时创建一个新的缓存。

  • 执行一些自定义的命令,此处以博客编译成静态文件需要的命令为例
name: Build
run: |
npm install
npm run build
  • 部署,这部分是整个核心的地方,编译完成后的项目会生成一个dist目录(nodejs项目), 或者其他设置好的名称
    • 使用 peaceiris/actions-gh-pages@v3 将静态页面部署成 GitHub Pages.
    • 配置token,有三种(github_token,deploy_token,personal_token), 具体 参考地址
      • personal_token 设置:
        • https://github.com/settings/tokens 添加一个token并配置权限
        • 找到需要用到token的仓库,进入 settings -> Secrets and variables -> Action 把刚刚创建好的token添加到这里,就可以在 ${{ secrets.名称 }} 访问了
    • 指定推送的分支和目录
    • 指定commit信息
name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
personal_token: ${{ secrets.ACCESS_TOKEN }}
publish_dir: ./build
publish_branch: gh-pages
full_commit_message: ${{ github.event.head_commit.message }}

· 8 min read
info

利用openssl颁发本地自签名SSL证书.以下脚本来自 github 略有改动

生成证书一共可以分为三步:

  1. 为当前CA生成根证书, 这个证书将被用来签署所有其他的证书,并且必须被这些服务器的客户所信任。(即安装到可信任证书颁发机构列表中去)
  2. 创建证书
  3. 签发证书

注意: 以下shell脚本在Ubuntu20.04成功执行, 其他平台暂未测试。(但Windows不行)

1. 根证书

new-root-ca.sh

#!/bin/bash
##
## new-root-ca.sh - create the root CA
## Copyright (c) 2000 Yeak Nai Siew, All Rights Reserved.
##

KEYBITS=4096
HASHALGO="sha256"
VALID_DAYS=3650
RANDOM_SRC=/dev/urandom

# Create the master CA key. This should be done once.
if [ ! -f ca.key ]; then
echo "No Root CA key found. Generating one"
openssl genrsa -aes256 -out ca.key -rand ${RANDOM_SRC} $KEYBITS
echo ""
fi

# Self-sign it.
CONFIG="root-ca.conf"
cat >$CONFIG <<EOT
[ req ]
default_bits = $KEYBITS
default_keyfile = ca.key
default_md = $HASHALGO
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
string_mask = nombstr
req_extensions = v3_req
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Shanghai
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Chris Yang Certificate Authority
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Certification Services Division
commonName = Common Name (eg, MD Root CA)
commonName_default = Chris Yang CA
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 40
[ v3_ca ]
basicConstraints = critical,CA:true
subjectKeyIdentifier = hash
[ v3_req ]
nsCertType = objsign,email,server
EOT

echo "Self-sign the root CA..."
openssl req -new -x509 -days ${VALID_DAYS} -config $CONFIG -key ca.key -out ca.crt
echo "complete."
rm -f $CONFIG

执行这段脚本后, 可以按提示输入一些信息, 如: 国家, 城市, 组织, 颁发机构等等, 这些就是对应了证书的一些信息。 接下来还有个输入密码的过程, 这个密码对应后面签发证书时的密码,需要牢记。执行完成后会生成两个文件:

  • ca.crt -- 分发给客户并添加到可信的CA数据库中
  • ca.key -- 私钥文件,保密不可泄漏

根证书有效期为十年,只要根证书不失效,执行以下两个命令重新签发证书即可

2. 创建证书

new-server-cert.sh

#!/bin/bash
##
## new-server-cert.sh - create the server cert
## Copyright (c) 2000 Yeak Nai Siew, All Rights Reserved.
##

KEYBITS=4096
HASHALGO="sha256"

# Create the key. This should be done once per cert.
CN=$1
if [ $# -lt 1 ]; then
echo "Usage: $0 <www.domain.com> [subjectAltName1 [san2 ...]]"
exit 1
fi

# force the CN to become a SAN even if no other SANs; Chrome compatibility
subjectAltNames="$*"

# if private key exists, ask if we want to generate a new key
if [ -f $CN.key ]; then
read -p "a key for this cn is already existing, generate a new one? " ANSWER
if [ "$ANSWER" == "Y" ] || [ "$ANSWER" == "y" ]; then
rm -f $CN.key
fi
fi

if [ ! -f $CN.key ]; then
echo "No $CN.key found. Generating one"
openssl genrsa -out $CN.key $KEYBITS
echo ""
fi

# Fill the necessary certificate data
CONFIG="server-cert.conf"
cat >$CONFIG <<EOT
[ req ]
default_bits = $KEYBITS
default_keyfile = server.key
default_md = $HASHALGO
distinguished_name = req_distinguished_name
string_mask = nombstr
req_extensions = v3_req
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Shanghai
localityName = Locality Name (eg, city)
localityName_default = Shanghai
0.organizationName = Organization Name (eg, company)
0.organizationName_default = optional
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = optional
commonName = Common Name (eg, www.domain.com)
commonName_default = $CN
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 40
[ v3_req ]
nsCertType = server
basicConstraints = critical,CA:false
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
EOT

# Handle optional Subject Alternate Names
if [ "$subjectAltNames" != "" ]; then
echo "subjectAltName = @alt_names" >> $CONFIG
echo "[alt_names]" >> $CONFIG
numi=1
numd=1
cn_already_added=0

# CN is added to the SAN list automatically
for san in $CN $subjectAltNames; do
# if CN has already been seen, skip it
if [ "$san" = "$CN" ]; then
if [ $cn_already_added -eq 0 ]; then
cn_already_added=1
else
continue #skip to next SAN
fi
fi

# determine if this looks like an IP or a DNS name
echo $san | egrep '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' &> /dev/null
if [ $? -eq 0 ]; then
echo "IP.$numi = $san" >> $CONFIG
let numi++
else
echo "DNS.$numd = $san" >> $CONFIG
let numd++
fi
done
fi

echo "Fill in certificate data"
openssl req -new -config $CONFIG -key $CN.key -out $CN.csr

rm -f $CONFIG

echo ""
echo "You may now run ./sign-server-cert.sh to get it signed"
# 创建单个证书
$ ./new-server-cert.sh www.test.org
$ ./new-server-cert.sh www.test.org a.test.org b.test.org
# 创建通配符证书
$ ./new-server-cert.sh '*.test.org'

执行shell后会创建出以下两个文件

  • www.test.org.csr -- CA的证书签署请求 (CSR是携带CA所需信息的文件,以便创建证书(CRT)文件,传递给服务器的客户。)
  • www.test.org.key -- 只存在网络服务器的私人加密密钥(证书文件)

3. 签发证书

sign-server-cert.sh

#!/bin/bash
##
## sign-server-cert.sh - sign using our root CA the server cert
## Copyright (c) 2000 Yeak Nai Siew, All Rights Reserved.
##

HASHALGO="sha256"
VALID_DAYS=365
RANDOM_SRC=/dev/urandom

CN=$1
if [ $# -ne 1 ]; then
echo "Usage: $0 <www.domain.com>"
exit 1
fi
if [ ! -f $CN.csr ]; then
echo "No $CN.csr found. You must create that first."
exit 1
fi
# Check for root CA key
if [ ! -f ca.key -o ! -f ca.crt ]; then
echo "You must have root CA key generated first."
exit 1
fi

# Sign it with our CA key #

# make sure environment exists
if [ ! -d ca.db.certs ]; then
mkdir ca.db.certs
fi
if [ ! -f ca.db.serial ]; then
echo '01' >ca.db.serial
fi
if [ ! -f ca.db.index ]; then
cp /dev/null ca.db.index
fi


# create the CA requirement to sign the cert
cat >ca.config <<EOT
[ ca ]
default_ca = default_CA
[ default_CA ]
dir = .
certs = \$dir
new_certs_dir = \$dir/ca.db.certs
database = \$dir/ca.db.index
serial = \$dir/ca.db.serial
RANDFILE = ${RANDOM_SRC}
certificate = \$dir/ca.crt
private_key = \$dir/ca.key
default_days = ${VALID_DAYS}
default_crl_days = 30
default_md = $HASHALGO
preserve = no
x509_extensions = server_cert
policy = policy_anything
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ server_cert ]
#subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
extendedKeyUsage = serverAuth,clientAuth,msSGC,nsSGC
basicConstraints = critical,CA:false
[req]
default_md = $HASHALGO
req_extensions = v3_req
[ v3_req ]
extendedKeyUsage = serverAuth, clientAuth
EOT

# Test for Subject Alternate Names
subjaltnames="`openssl req -text -noout -in $CN.csr | sed -e 's/^ *//' | grep -A1 'X509v3 Subject Alternative Name:' | tail -1 | sed -e 's/IP Address:/IP:/g'`"
if [ "$subjaltnames" != "" ]; then
echo "Found subject alternate names: $subjaltnames"
echo ""
echo "subjectAltName = $subjaltnames" >> ca.config
fi

# revoke an existing old certificate
if [ -f $CN.crt ]; then
echo "Revoking current certificate: $CN.crt"
openssl ca -revoke $CN.crt -config ca.config
fi

# sign the certificate
echo "CA signing: $CN.csr -> $CN.crt:"
openssl ca -config ca.config -extensions v3_req -out $CN.crt -infiles $CN.csr
echo ""
echo "CA verifying: $CN.crt <-> CA cert"
openssl verify -CAfile ca.crt $CN.crt
echo ""

# cleanup after SSLeay
rm -f ca.config
rm -f ca.db.serial.old
rm -f ca.db.index.old
$ ./sign-server-cert.sh a.test.org

签发证书完成后,会生成 a.test.org.crt。使用以下命令能查看证书有效期

$ cat a.test.org.crt | openssl x509 -noout -enddate

签发证书时, 需要输入创建根证书时设置的密码

4. 使用

签发证书后, 会在当前目录生成 *.test.org.key*.test.org.crt 两个文件, 在nginx中配置后即可使用

ssl_certificate /etc/ssl/certs/a.text.org.crt;
ssl_certificate_key /etc/ssl/private/a.text.org.key;

· 3 min read

内连接 (inner join)

内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行,组合成新的记录。 在内连接查询中,只有满足条件的记录才能出现在结果关系中

如果在一个连接查询中,涉及的两个表都是同一个表,这种查询称为自连接查询。自连接是一种特殊的内连接,它是指相互连接的表在物理上为同一张表,但可以在逻辑上分为两张表

select * from a inner join b on a.x = b.x;

外连接 (outer join)

外连接查询将查询多个表中相关联的行,内连接时,返回查询结果集合中仅是符合查询条件和连接条件的行

外连接分为左连接右连接

  • 左连接(left join): 返回包括左表中的所有记录和右表中连接字段相等的记录。左表没有右表中的值则用null填充
  • 右连接(right join): 返回包括右表中的所有记录和左表中连接字段相等的记录。右表没有左表中的值则用null填充
# 左连接
select * from a left join b on a.x = b.x;
# 右连接
select * from a right join b on a.x = b.x;

自然连接 (natural join)

自然连接是特殊的等值连接,不需要指定连接的条件,会根据左右连接表中相同的字段作为连接条件

select * from a natural join b;

交叉连接 (cross join)

交叉连接主要用于生成笛卡尔积

select * from a natural join b;

连接查询注意点

  • 查询数据尽可能的加入条件,减少join后的数据集大小
  • join时要选择小表驱动大表 (大表驱动小表会导致外层循环过多,浪费资源)
for (大表) {
for (小表) { }
}

如何判断:

  1. left join左边表是驱动表
  2. right join右边表是驱动表
  3. inner join会自动选择数据量少的作为驱动表
  4. 查询 explain 执行计划,第一个表即驱动表

· 2 min read
info

Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,可用于在Linux与Windows系统文件共享和打印共享

安装

$ sudo apt-get install samba samba-common

配置

# 配置一个用户(执行后输入密码即可)
$ sudo smbpasswd -a username

# 修改配置文件
$ sudo vim /etc/samba/smb.conf

在配置文件最后一行加入以下配置内容

[file] # 访问路径
comment = File path
browseable = yes
path = /home/xxx # 需要共享的目录
create mask = 0755
directory mask = 0755
valid users = username # 配置的用户名
public = yes
available = yes
writable = yes

启动

修改配置后需要重启服务

$ sudo /etc/init.d/smbd restart

使用

输入服务IP地址 + 配置的访问路径,如: 10.0.0.x/file 输入用户名和密码就可以了

· One min read
info

编译二进制文件在Linux安装最新版本Python3

二进制文件下载地址:https://www.python.org/ftp/python

下载

$ wget https://www.python.org/ftp/python/3.10.0/Python-3.10.0.tgz
# 解压文件
$ tar -zxf Python-3.10.0.tgz

编译 & 安装

同时安装有2.x和3.x版本配置 需要配置不同路径区分 /usr/local/python3, 如果只有一个版本 prefix=/usr/local/python 即可

$ cd Python-3.10.0 && ./configure prefix=/usr/local/python3
$ make && make install

创建软链

$ ln -s /usr/local/python3/bin/python3 /usr/bin/python3
$ ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3

不创建软链也可以,但是得把 /usr/local/python3/bin 目录加入环境变量

验证

$ python3 --version
Python 3.10.0

· 6 min read

从 UCI 上下载数据集

鸢尾花特征集

实现方法一

调用 scikit-learn 库来完成分类

from sklearn.naive_bayes import GaussianNB  # 高斯朴素贝叶斯
from sklearn.model_selection import train_test_split # 训练集测试集划分
from sklearn.metrics import accuracy_score # 计算准确率
# 导入数据集 <鸢尾花>
from sklearn import datasets


def sk_learn():
"""
方式一,直接调用 sk_learn类库
:return:
"""
iris = datasets.load_iris()

# 分类数据
# print(iris.data)
# 标签
# print(iris.target)

# 切分数据集
trainSet, testSet, trainTarget, testTarget = train_test_split(iris.data, iris.target, random_state=37)

# 建模
clf = GaussianNB()
# 传入数据和标签
clf.fit(trainSet, trainTarget)

# 在测试集上进行预测,proba导出的是每个样本属于某类的概率
predict = clf.predict(testSet)
clf.predict_proba(testSet)

target_name = iris.target_names

# 测试准确率
print("预测结果准确率:{}".format(accuracy_score(testTarget, predict)))

for i in range(len(testSet)):
print("{}===>{}".format(testSet[i], target_name[predict[i]]))


if __name__ == '__main__':
sk_learn()
pass
OUT
D:\Anaconda3\envs\TF2.1\python.exe naive_bayes.py
预测结果准确率:1.0
[6.4 2.9 4.3 1.3]===>versicolor
[5.2 3.5 1.5 0.2]===>setosa
[6.5 3. 5.8 2.2]===>virginica
[5.8 2.7 5.1 1.9]===>virginica
[4.6 3.4 1.4 0.3]===>setosa
[5.6 2.7 4.2 1.3]===>versicolor
[4.7 3.2 1.3 0.2]===>setosa
[5.7 2.5 5. 2. ]===>virginica
[6.9 3.2 5.7 2.3]===>virginica
[6. 3. 4.8 1.8]===>virginica
[5.4 3.9 1.7 0.4]===>setosa
[5.9 3. 5.1 1.8]===>virginica
[6.7 3.1 5.6 2.4]===>virginica
[5.3 3.7 1.5 0.2]===>setosa
[4.3 3. 1.1 0.1]===>setosa
[6.5 3. 5.5 1.8]===>virginica
[5.6 2.5 3.9 1.1]===>versicolor
[6.4 3.1 5.5 1.8]===>virginica
[6.8 3. 5.5 2.1]===>virginica
[6.9 3.1 5.4 2.1]===>virginica
[5.6 3. 4.5 1.5]===>versicolor
[5.4 3.7 1.5 0.2]===>setosa
[6.7 3.3 5.7 2.1]===>virginica
[6.3 2.5 4.9 1.5]===>versicolor
[4.9 3.6 1.4 0.1]===>setosa
[5.7 2.9 4.2 1.3]===>versicolor
[7.2 3.2 6. 1.8]===>virginica
[5.2 2.7 3.9 1.4]===>versicolor
[5.1 3.3 1.7 0.5]===>setosa
[5.7 3. 4.2 1.2]===>versicolor
[4.6 3.1 1.5 0.2]===>setosa
[6.3 2.5 5. 1.9]===>virginica
[5.7 2.6 3.5 1. ]===>versicolor
[5. 3.5 1.3 0.3]===>setosa
[6.3 3.4 5.6 2.4]===>virginica
[7.1 3. 5.9 2.1]===>virginica
[6.3 2.3 4.4 1.3]===>versicolor
[6.6 3. 4.4 1.4]===>versicolor

Process finished with exit code 0

实现方式二

手写

# 方式二:
import pandas as pd
import numpy as np
import random


def load_dataSet():
csv = pd.read_csv('iris.data', header=None)
return csv
pass


def randSplit(dataSet, rate):
"""
随机打乱数据集
:param dataSet: 数据集
:param rate: 提取训练集和测试集的比率
:return: train test
"""
l_index = list(dataSet.index) # 提取出索引
random.shuffle(l_index) # 随机打乱索引
dataSet.index = l_index # 将打乱后的索引重新赋值给原数据集
n = dataSet.shape[0] # 总行数
m = int(n * rate) # 训练集的数量
train = dataSet.loc[range(m), :] # 提取前m个记录作为训练集
test = dataSet.loc[range(m, n), :] # 剩下的作为测试集
dataSet.index = range(dataSet.shape[0]) # 更新原数据集的索引
test.index = range(test.shape[0]) # 更新测试集的索引
return train, test


def gnb_classify(train, test):
"""
分类
:param train: 训练集
:param test: 测试集
:return:
"""
labels = train.iloc[:, -1].value_counts().index # 提取训练集的标签种类
mean = [] # 存放每个类别的均值
std = [] # 存放每个类别的方差
result = [] # 存放测试集的预测结果
for i in labels:
item = train.loc[train.iloc[:, -1] == i, :] # 分别提取出每一种类别
m = item.iloc[:, :-1].mean() # 当前类别的平均值
s = np.sum((item.iloc[:, :-1] - m) ** 2) / (item.shape[0]) # 方差
mean.append(m)
std.append(s)
means = pd.DataFrame(mean, index=labels)
stds = pd.DataFrame(std, index=labels)

for j in range(test.shape[0]):
iset = test.iloc[j, :-1].tolist() # 当前测试实例
iprob = np.exp(-1 * (iset - means) ** 2 / (stds * 2)) / (np.sqrt(2 * np.pi * stds)) # 正态分布公式
prob = 1 # 初始化当前实例总概率

for k in range(test.shape[-1] - 1):
prob *= iprob[k]
cla = prob.index[np.argmax(prob.values)] # 返回最大概率的类别
result.append(cla)
test['predict'] = result
acc = (test.iloc[:, -1] == test.iloc[:, -2]).mean() # 计算预测准确率
print("预测准确率: {}".format(acc))
# 输入预测的结果
print(test)


if __name__ == '__main__':
data_set = load_dataSet()
# 给 80% 的数据用来训练,20% 用来测试
_train, _test = randSplit(data_set, 0.8)
gnb_classify(_train, _test)
pass
OUT
D:\Anaconda3\envs\TF2.1\python.exe /naive_bayes.py
预测准确率: 0.9666666666666667
0 1 2 3 4 predict
0 6.8 2.8 4.8 1.4 Iris-versicolor Iris-versicolor
1 7.4 2.8 6.1 1.9 Iris-virginica Iris-virginica
2 4.6 3.1 1.5 0.2 Iris-setosa Iris-setosa
3 7.0 3.2 4.7 1.4 Iris-versicolor Iris-versicolor
4 6.3 2.3 4.4 1.3 Iris-versicolor Iris-versicolor
5 6.9 3.2 5.7 2.3 Iris-virginica Iris-virginica
6 6.0 2.2 5.0 1.5 Iris-virginica Iris-versicolor
7 6.3 3.3 4.7 1.6 Iris-versicolor Iris-versicolor
8 5.5 2.6 4.4 1.2 Iris-versicolor Iris-versicolor
9 5.4 3.9 1.7 0.4 Iris-setosa Iris-setosa
10 5.8 4.0 1.2 0.2 Iris-setosa Iris-setosa
11 6.5 3.0 5.2 2.0 Iris-virginica Iris-virginica
12 5.2 3.5 1.5 0.2 Iris-setosa Iris-setosa
13 6.3 2.5 4.9 1.5 Iris-versicolor Iris-versicolor
14 7.7 3.0 6.1 2.3 Iris-virginica Iris-virginica
15 5.4 3.4 1.5 0.4 Iris-setosa Iris-setosa
16 5.0 3.6 1.4 0.2 Iris-setosa Iris-setosa
17 5.1 3.8 1.9 0.4 Iris-setosa Iris-setosa
18 5.7 2.9 4.2 1.3 Iris-versicolor Iris-versicolor
19 7.2 3.2 6.0 1.8 Iris-virginica Iris-virginica
20 4.8 3.0 1.4 0.3 Iris-setosa Iris-setosa
21 4.3 3.0 1.1 0.1 Iris-setosa Iris-setosa
22 5.0 3.4 1.6 0.4 Iris-setosa Iris-setosa
23 5.1 3.8 1.5 0.3 Iris-setosa Iris-setosa
24 6.0 2.7 5.1 1.6 Iris-versicolor Iris-versicolor
25 5.5 2.3 4.0 1.3 Iris-versicolor Iris-versicolor
26 5.0 3.3 1.4 0.2 Iris-setosa Iris-setosa
27 5.5 2.5 4.0 1.3 Iris-versicolor Iris-versicolor
28 5.4 3.9 1.3 0.4 Iris-setosa Iris-setosa
29 6.4 2.8 5.6 2.1 Iris-virginica Iris-virginica

Process finished with exit code 0

问题:训练数是随机筛选的,每次运行的准确率不一样