数据分析_三大神器使用笔记整理

文章目录

三大神器

Numpy

支持常见的矩阵操作,通过ndarray类实现了对多位数据的封装,提供了对这些数组操作的方法和函数集.

由于Numpy支持并行运算的功能,因此使用多核CPU的时候,Numpy会自动进行并行运算

Pandas

Pandas 的主要数据结构是 Series (一维数据)与 DataFrame(二维数据).

Series类似于一维数组的对象,它由一组数据(各种Numpy数据类型)以及一组与之相关的数据标签(即索引)组成.

DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共同用一个索引).

Matplotlib

绘图用的

Seaborn:基于Matplotlib的图形可视化工具,能让用户 做出各种统计图表

Seaborn的底层是基于Matplotlib的,他们的差异有点像在点餐时选套餐还是自己点的区别,Matplotlib是独立点菜,可能费时费心,但最后上桌的菜全是特别适合自己的;而Seaborn是点套餐,特别简单,一切都是配好的,虽然省时省心,但可能套餐里总有些菜是不那么合自己口味的。

Seaborn是用户把自己常用到的可视化绘图过程进行了函数封装,形成的一个“快捷方式”,他相比Matplotlib的好处是代码更简洁,可以用一行代码实现一个清晰好看的可视化输出。主要的缺点则是定制化能力会比较差,只能实现固化的一些可视化模板类型;

而Matplotlib是可以实现高度定制化绘图的,高度定制化可以让你获得最符合心意的可视化输出结果,但也因此需要设置更多的参数,因而代码更加复杂一些。

引入方式
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

魔法方法和忽略警告

忽略警告
import warning
#忽略所有的警告信息
warnings.filterwarnings('ignore')
魔法方法
%pwd
# 显示当前文件路径
% ls
# 显示文件详情

Numpy

Numerical Python

一维
方法一
# 法1
nums = [1, 2, 3, 4, 155]
array1 = np.array(nums, dtype='u8')
array1
# array([  1,   2,   3,   4, 155], dtype=uint64)

# 数组的类型
type(array1)
# numpy.ndarray

# 数组的size属性 - 元素的个数
array1.size
# 5

# 数组的dtype属性 - 元素的数据类型
array1.dtype
# dtype('uint64')

# 数组的shape属性 - 数组的形状
array1.shape
# (5,)

# 数组的ndim属性 - 数组的维度(ndimension)
array1.ndim
# 1

# 数组的itemsize属性 - 每个元素占用的内存空间的大小(字节)
array1.itemsize
# 8

# 数组的nbytes属性 - 整个数组占用的内存空间的大小(字节)
array1.nbytes
# 40

# 将数组还原成Python中的列表
array1.tolist()
# [1, 2, 3, 4, 155]

方法二
array2 = np.arange(1,100,2)
#array([ 1,  3,  5,  7,  9, 11, 13, 15, #17, 19, 21, 23, 25, 27, 29, 31, 33,
#       35, 37, 39, 41, 43, 45, 47, 49, #51, 53, 55, 57, 59, 61, 63, 65, 67,
#       69, 71, 73, 75, 77, 79, 81, 83, #85, 87, 89, 91, 93, 95, 97, 99])

方法三
array3 = np.linspace(-5,5,num=100,endpoint=False)
#array([-5. , -4.9, -4.8, -4.7, -4.6, -4.5, -4.4, -4.3, -4.2, -4.1, -4. ,
#       -3.9, -3.8, -3.7, -3.6, -3.5, -3.4, -3.3, -3.2, -3.1, -3. , -2.9,
#       -2.8, -2.7, -2.6, -2.5, -2.4, -2.3, -2.2, -2.1, -2. , -1.9, -1.8,
#       -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1. , -0.9, -0.8, -0.7,
#       -0.6, -0.5, -0.4, -0.3, -0.2, -0.1,  0. ,  0.1,  0.2,  0.3,  0.4,
#        0.5,  0.6,  0.7,  0.8,  0.9,  1. ,  1.1,  1.2,  1.3,  1.4,  1.5,
#        1.6,  1.7,  1.8,  1.9,  2. ,  2.1,  2.2,  2.3,  2.4,  2.5,  2.6,
#        2.7,  2.8,  2.9,  3. ,  3.1,  3.2,  3.3,  3.4,  3.5,  3.6,  3.7,
#        3.8,  3.9,  4. ,  4.1,  4.2,  4.3,  4.4,  4.5,  4.6,  4.7,  4.8,
#        4.9])
方法四
array4 = np.random.randint(60, 101, 15)
#array([95, 67, 78, 63, 84, 79, 87, 60, 91, 65, 92, 66, 93, 73, 70])
二维
方法一
nums = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
array8 = np.array(nums)
#array([[1, 2, 3],
#       [4, 5, 6],
#       [7, 8, 9]])
方法二
# 将一维数组调形成二维数组
array9 = array4.reshape((5, 3))
#array([[95, 67, 78],
#       [63, 84, 79],
#       [87, 60, 91],
#       [65, 92, 66],
#       [93, 73, 70]])
方法三
array11 = np.random.randint(60, 101, size=(5, 3))
#array([[66, 70, 91],
#       [98, 79, 76],
#       [84, 70, 79],
#       [76, 91, 85],
#       [88, 73, 84]])
方法四
np.zeros((3, 3), dtype='i8')
#array([[0, 0, 0],
#       [0, 0, 0],
#       [0, 0, 0]], dtype=int64)
np.ones((2, 2), dtype='i4')
#array([[1, 1],
#       [1, 1]])
np.full((3, 3), 5)
#array([[5, 5, 5],
#       [5, 5, 5],
#       [5, 5, 5]])
方法五
# 创建二维数组对象(方法五)---> 单位矩阵
np.eye(3, dtype='i8')
#array([[1, 0, 0],
#       [0, 1, 0],
#       [0, 0, 1]], dtype=int64)
通过array把列表处理成数组
import random
scores = [[random.randrange(60, 101) for _ in range(3)] for _ in range(5)]
scores
# [[60, 83, 100], [77, 81, 92], [61, 100, 89], [93, 82, 94], [94, 68, 60]]
# 通过array把列表处理成数组
scores = np.array(scores)
scores
# array([[ 60,  83, 100],
#       [ 77,  81,  92],
#       [ 61, 100,  89],
#       [ 93,  82,  94],
#       [ 94,  68,  60]])
沿着1轴求平均
scores.mean(axis=1)
#[81.         83.33333333 83.33333333 89.66666667 74.        ]
沿着0轴找最大
scores.max(axis=0)
# array([ 94, 100, 100])
scores.min(axis=0)
# array([60,68,60])
数组的索引和切片
# 一维
array12 = np.random.randint(1, 100, 9)
# array([35, 23, 95, 85, 12, 26, 14, 22,  9])

# 二维
array13 = np.arange(1, 10).reshape((3, 3))
array13
#array([[1, 2, 3],
#       [4, 5, 6],
#       [7, 8, 9]])
普通索引

可以使用正向索引和负向索引,注意不要越界

array12[0], array12[8], array12[-1], array12[-9]
# 取出来是个元组
# (35, 9, 9, 35)

array13[0][1], array13[1][2]
#(2, 6)
布尔索引

使用布尔类型的数组充当索引,True对应的元素会保留,False对应的元素会过滤掉

array12[array12 > 50]
# array([100, 200,  95,  85, 300])

array12 % 2 != 0
# array([False, False,  True,  True, False, False, False, False, False])
array12[array12 % 2 != 0]
# array([95, 85])
# 从数组中取大于50的奇数
# 两个布尔数组是可以做逻辑运算(对应元素进行逻辑运算),但是不能用Python中的and、or、not
# 取而代之的是三个运算符:&、|、~
array12[(array12 > 50) & (array12 % 2 != 0)]
花式索引

(fancing index)- 一次性操作多个索引对应的元素

# 一维
array12[[0, 2, 5, 7, 0, 0, -1, -1]]
# array([35, 95, 26, 22, 35, 35,  9,  9])

# 二维
array13[[0, 0, 1, 2, 2], [1, 2, 2, 2, 0]]
# array([2, 3, 6, 9, 7])
切片
# 切片
# 一维
array12[[0, 1, -1]] = 100, 200, 300
#array([100, 200,  95,  85,  12,  26,  14,  22, 300])
array12[::-1]
#array([300,  22,  14,  26,  12,  85,  95, 200, 100])

# 二维
array13[:2, 1:]
# 切到第二行,从第一列开始
#array([[2, 3],
#       [5, 6]])

array13[2, :]
# 从第二行开始,全切
array([7, 8, 9])

array13[::2, ::2]
# 间隔二行二列地切
#array([[1, 3],
#       [7, 9]])
数组对象的方法
array14 = np.random.randint(100, 501, 12)
array14
# array([489, 475, 172, 436, 277, 338, 456, 303, 255, 430, 125, 391])
获取描述性统计信息
均值
# 均值(集中趋势)---> 均值容易受到极端值的干扰
# 如何区分一个数据是正常的极端值还是异常值,主要的点是看数据是否能够重复稳定的出现
print(array14.mean())
print(np.mean(array14))
#345.5833333333333
#345.5833333333333
求和
array14.sum()
# 4147
排序
# numpy中的sort函数跟Python中的sorted类似,不会对原数组排序,而是返回排序后的数组对象
# 这种设计理念叫做函数的无副作用性设计(函数不能随意修改传入的对象的状态)
np.sort(array14)
# array([125, 172, 255, 277, 303, 338, 391, 430, 436, 456, 475, 489])
中位数
np.median(array14)
# 364.5
极值
print(array14.max())
print(np.max(array14))
print(array14.min())
print(np.min(array14))
#489
#489
#125
#125
极差
print(array14.max() - array14.min())
print(np.ptp(array14))
#364
#364
方差(variance)和标准差(standard deviation)

总体方差:
σ 2 = 1 N ∑ i = 1 N ( X i − μ ) 2 \sigma^2 = \frac{1}{N} \sum_{i=1}^N (X_i - \mu)^2 σ2=N1i=1N(Xiμ)2

样本方差:
S 2 = 1 N − 1 ∑ i = 1 N ( X i − X ˉ ) 2 S^2 = \frac{1}{N - 1} \sum_{i=1}^N (X_i - \bar{X})^2 S2=N11i=1N(XiXˉ)2

总体标准差:
σ = 1 N ∑ i = 1 N ( X i − μ ) 2 \sigma = \sqrt{\frac{1}{N} \sum_{i=1}^N (X_i - \mu)^2} σ=N1i=1N(Xiμ)2

样本标准差:
S = 1 N − 1 ∑ i = 1 N ( X i − X ˉ ) 2 S = \sqrt{\frac{1}{N - 1} \sum_{i=1}^N (X_i - \bar{X})^2} S=N11i=1N(XiXˉ)2

print(array14.var())
print(array14.std())
# 13370.07638888889
# 115.62904647574021
四分位距离

补充:四分位距离除了可以反映数据的集中或离散趋势外,还可以帮我们判定一个数据是否为异常值(极端值)。
D a t a < Q 1 − 1.5 × I Q R Data < Q1 - 1.5 \times IQR Data<Q11.5×IQR

D a t a > Q 3 + 1.5 × I Q R Data > Q3 + 1.5 \times IQR Data>Q3+1.5×IQR

# 上四分位数(75%分位数)
q3 = np.quantile(array14, 0.75)
# 下四分位数(25%分位数)
q1 = np.quantile(array14, 0.25)
# 四分位距离
iqr = q3 - q1
iqr
# 169.5
数组类型转化
array15 = array14.astype(np.float64)
print(array15)
print(array15.dtype)
print(array14.dtype)
#[489. 475. 172. 436. 277. 338. 456. 303. 255. 430. 125. 391.]
#float64
#int32
数组的序列化和反序列化
# json数据的保存
import json
person = {'name': 'Hao', 'age': 41}
with open('person.json', 'w') as file:
    json.dump(person, file)
# 产生了一个叫person的文件
# 将数组做pickle序列化并保存到文件中
array15.dump('array15')
# 产生了一个叫array15的文件
# 从文件中加载数组对象(pickle反序列化)
array16 = np.load('array15', allow_pickle=True)
array16
#array([489., 475., 172., 436., 277., 338., 456., 303., 255., 430., 125.,
#       391.])

array15 is array16
# False
array15 == array16
# array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
#        True,  True,  True])
数组的调形
扁平化数组

array13.flatten()
# array([1, 2, 3, 4, 5, 6, 7, 8, 9])
调形
array15.reshape((4, 3))
#array([[489., 475., 172.],
#       [436., 277., 338.],
#       [456., 303., 255.],
#       [430., 125., 391.]])
调形补0
array16 = array15.copy()
# 调整数组的大小(在数组上直接调整,缺失的值补0)
array16.resize((5, 3))
array16
#array([[489., 475., 172.],
#       [436., 277., 338.],
#       [456., 303., 255.],
#       [430., 125., 391.],
#       [  0.,   0.,   0.]])
调形补原数组

(返回新的数组对象,缺失的值用原数组的值填充)

np.resize(array15, (7, 3))
#array([[489., 475., 172.],
#       [436., 277., 338.],
#       [456., 303., 255.],
#       [430., 125., 391.],
#       [489., 475., 172.],
#       [436., 277., 338.],
#       [456., 303., 255.]])
排序
# 就地排序(直接修改原数组)
array15.sort()
array15

和np.sort不一样的是.array.sort是就地修改

数组的运算
array17 = np.arange(1, 15, 2)
# array([ 1,  3,  5,  7,  9, 11, 13])
array18 = np.array([2, 2, 3, 3, 4, 4, 5])
# array([2, 2, 3, 3, 4, 4, 5])
array17 * array18
# array([ 2,  6, 15, 21, 36, 44, 65])
数组的广播机制

广播(broadcast)机制:当两个形状并不相同的数组进行运算时,NumPy会试图通过广播机制使得两个数组的形状变得一致。满足下列条件之一,才能使用广播机制:

  1. 两个数组的后缘维度(shape属性从后往前看)相同;
  2. 两个数组的后缘维度不同,但有一方的维度为1。
array19 = np.array([[1, 2, 3], [4, 5, 6]])
# array([[1, 2, 3],
#       [4, 5, 6]])
array20 = np.array([10, 20, 30])
array20
# array([10, 20, 30])
array19+array20
# array([[11, 22, 33],
#       [14, 25, 36]])
array21 = np.array([[40], [40]])
# array([[40],
#       [40]])

array19 + array21
# array([[41, 42, 43],
#       [44, 45, 46]])
其他函数
# nan ---> Not a Number
# inf ---> Infinity
temp = np.array([1, 2, np.nan, 3, -np.inf])
temp
# array([  1.,   2.,  nan,   3., -inf])
空值和无穷大
# isnan:判断有没有空值,产生布尔数组
temp[~np.isnan(temp)]
# array([  1.,   2.,   3., -inf])
# isinf:判断无穷大值,产生布尔数组
np.isinf(temp)
# array([False, False, False, False,  True])
去重
array1 = np.array([1, 1, 2, 3, 5, 1, 1, 3, 5, 4])
# 去重
np.unique(array1)
# array([1, 2, 3, 4, 5])
数组拼接
array2 = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
array3 = np.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])
水平拼接
np.hstack((array2, array3))
#array([[1, 1, 1, 4, 4, 4],
#       [2, 2, 2, 5, 5, 5],
#       [3, 3, 3, 6, 6, 6]])
垂直拼接
np.vstack((array2, array3))
#array([[1, 1, 1],
#       [2, 2, 2],
#       [3, 3, 3],
#       [4, 4, 4],
#       [5, 5, 5],
#       [6, 6, 6]])
拼接两个数组
array4 = np.stack((array2, array3))
array4
# array([[[1, 1, 1],
#        [2, 2, 2],
#        [3, 3, 3]],
#
#       [[4, 4, 4],
#        [5, 5, 5],
#        [6, 6, 6]]])
array4.shape
#(2,3,3)
按轴拼接
array5 = np.stack((array2, array3), axis=1)
#array([[[1, 1, 1],
#        [4, 4, 4]],
#
#       [[2, 2, 2],
#        [5, 5, 5]],
#
#       [[3, 3, 3],
#        [6, 6, 6]]])
array5.shape
# (3, 2, 3)
# 数组拼接
# axis=0 ---> 相当于是vstack
# axis=1 ---> 相当于是hstack
array6 = np.concatenate((array2, array3), axis=1)
array6
#array([[1, 1, 1, 4, 4, 4],
#       [2, 2, 2, 5, 5, 5],
#       [3, 3, 3, 6, 6, 6]])
拆数组
# 在水平方向拆分数组,拆成俩
np.hsplit(array6, 2)
#[array([[1, 1, 1],
#        [2, 2, 2],
#        [3, 3, 3]]),
# array([[4, 4, 4],
#        [5, 5, 5],
#        [6, 6, 6]])
#]
# 在垂直方向拆,拆3个
np.vsplit(array6, 3)
#[array([[1, 1, 1, 4, 4, 4]]),
# array([[2, 2, 2, 5, 5, 5]]),
# array([[3, 3, 3, 6, 6, 6]])]
# 等同于
np.split(array6, 3, axis=0)
追加元素(如果不指定axis参数,原数组会被扁平化)
np.append(array6, 7)
# array([1, 1, 1, 4, 4, 4, 2, 2, 2, 5, 5, 5, 3, 3, 3, 6, 6, 6, 7])
插入元素(不指定axis,原数组会被扁平化)
np.insert(array6, 0, 8)
# array([8, 1, 1, 1, 4, 4, 4, 2, 2, 2, 5, 5, 5, 3, 3, 3, 6, 6, 6])
按条件抽取数据构成新数组(相当于布尔索引)
array7 = np.random.randint(1, 100, 10)
# array([16, 96, 89, 95, 42,  3, 22, 54, 86, 51])
np.extract(array7 > 50, array7)
# array([96, 89, 95, 54, 86, 51])

conds = [array7 < 30, array7 > 50]
choices = [array7 ** 2, array7 - 10]
# 按照设定的条件和处理数据的方式从数组中获取数据构成新数组
np.select(conds, choices)
设定条件,将满足条件和不满足条件的数据按照指定的方式处理后构成新数组
np.where(array7 > 50, array7, array7 ** 2)
#array([ 256,   96,   89,   95, 1764,    9,  484,   54,   86,   51])
pandas显示所有行/列
#设置value的显示长度为200,默认为50
pd.set_option('max_colwidth',200)
#显示所有列,把行显示设置成最大
pd.set_option('display.max_columns', None)
#显示所有行,把列显示设置成最大
pd.set_option('display.max_rows', None)
矩阵操作支持

官方已经不建议使用matrix对象,建议使用的是普通ndarray对象表示矩阵
而且在将来的版本中,matrix类型可能会被移除掉

创建matrix对象(方法一)
m1 = np.matrix('1 2 3; 4 5 6')
m1
#matrix([[1, 2, 3],
#        [4, 5, 6]])
创建matrix对象(方法二)
m2 = np.matrix([[2, 2], [3, 3], [4, 4]])
m2
#matrix([[2, 2],
#        [3, 3],
#        [4, 4]])
创建matrix对象(方法三)
m3 = np.matrix(np.arange(1, 10).reshape(3, 3))
m3
# matrix([[1, 2, 3],
#        [4, 5, 6],
#        [7, 8, 9]])
获得matrix对象对应的ndarray对象
# 获得matrix对象对应的ndarray对象
m3.A
# array([[1, 2, 3],
#       [4, 5, 6],
#       [7, 8, 9]])
获得matrix对象对应的一维数组
m3.A1
# array([1, 2, 3, 4, 5, 6, 7, 8, 9])
转置
m3.T
#matrix([[1, 4, 7],
#        [2, 5, 8],
#        [3, 6, 9]])

计算行列式的值
np.linalg.det(m3)
# m3 ---> Singular matrix ---> 奇异矩阵 ---> 无法求逆矩阵
# m3.I
求逆矩阵
m5 = m4.I
求乘积

和ndarray相乘不一样

m4.A * m5.A
实现矩阵乘法的函数
# matmul ---> 实现矩阵乘法的函数
np.round(np.matmul(a4, a5), 1)
两个数组做矩阵乘法运算的运算符
np.round(a4 @ a5, 1)

求解下面的线性方程组:用np求解的时候,只能求一个解的情况,无穷解和一个解都会报错
{ 3 x 1 + 5 x 2 = 4 x 1 + 2 x 2 = 1 {\begin{cases} 3x_{{1}}+5x_{{2}}=4\\ x_{{1}}+2x_{{2}}=1 \end{cases}} {3x1+5x2=4x1+2x2=1

a = np.array([[3, 5], [1, 2]])
b = np.array([4, 1]).reshape(-1, 1)
# 解线性方程组
np.linalg.solve(a, b)
# 无解 ---> LinAlgError ---> Singular matrix
# a = np.array([[1, 1], [2, 2]])
# b = np.array([2, 1]).reshape(-1, 1)
# np.linalg.solve(a, b)
# 无穷解 ---> LinAlgError ---> Singular matrix
# a = np.array([[1, 1], [2, 2]])
# b = np.array([2, 4]).reshape(-1, 1)
# np.linalg.solve(a, b)
# 线性方程组有解的条件 ---> 系数矩阵(|A|)的秩和增广矩阵(|Ab|)的秩相等
# 矩阵的秩 ---> 线性无关(不能通过其他向量做线性变换得到)的行向量的个数
# 计算矩阵的秩
np.linalg.matrix_rank(a)
# 获得增广矩阵
ab = np.hstack((a, b))
np.linalg.matrix_rank(ab)
a = np.array([[3, 1, 2], [1, -1, 4], [2, 0, 3]])
b = np.array([0, 0, 0]).reshape(-1, 1)
ab = np.hstack((a, b))
print(np.linalg.matrix_rank(a))
print(np.linalg.matrix_rank(ab))

Pandas

Panel Data Set

Pandas中文网

三个核心的数据类型:

  1. Series —> 数据系列 —> 表示一维的数据
  2. DataFrame —> 数据框/数据窗/数据表 —> 表示二维的数据(表格)
  3. Index —> 索引 —> 为SeriesDataFrame对象提供索引服务
DataFrame对象
  1. 创建DataFrame对象

    • read_csv:读取CSV文件创建DataFrame
    • read_excel:读取Excel文件创建DataFrame(需要openpyxlxlwtxlrd
    • read_sql:从关系型数据库中读取数据创建DataFrame(读MySQL的数据需要pymysqlmysqlclient
  2. DataFrame索引和切片

    • 获取列:df['col_name'] / df.col_name —> Series

    • 获取行:df.loc[row_index] / df.iloc[0] —> Series

    • 获取单元格:df.at[row_index, col_name] / df.iat[0, 1] —> 值

    • 切片:df.loc[row_index1:row_index2] / df.iloc[m:n:k] / df[m:n:k]

    • 花式索引/布尔索引

    • 基本方法:

      • info():查看DataFrame的相关信息
      • head() / tail():查看前N条或后N条数据

df.loc和df.iloc的区别:

  1. loc使用范围比iloc更广泛实用,loc可以使用切片,名称(index,columns),也可以切片和名称混合使用;

  2. loc不能使用不存在的索引充当切片取值,如:df.loc[-1]

  3. iloc只能用整数来取值

    可以把i看作是int

  4. loc的使用范围高于iloc,loc也可以做到iloc的切片取数,除了df.loc[-1]

    使用切片\索引\列名称查找.

    按条件查找

    # 切片
    hy_data.loc[2:4,]
    hy_data.loc[[0,1,2,3],:]
    # 切片和名称混合
    hy_data.loc[2:4,'用户编号']
    # 按条件取数
    # 表示取出df中“行业分类”列值的长度等于6的所有行
    hy_data.loc[hy_data['行业分类'].str.len() == 6, :]
    

data就是表格中的数据,columns是列索引,index是行索引

df = pd.DataFrame(data=scores, columns=courses, index=names)
df
#   	语文	数学	英语
# 白起	60	83	100
# 安琪拉	77	81	92
# 庄周	61	100	89
# 狄仁杰	93	82	94
# 李元芳	94	68	60
创建方式
方式一:基于二维数组
# 创建DataFrame的第一种方式(基于二维数组)
df1 = pd.DataFrame(
    data=np.random.randint(60, 101, (5, 3)),
    index=np.arange(1001, 1006),
    columns=['语文', '数学', '英语']
)
df1
#   	语文	数学	英语
# 1001	67	91	77
# 1002	82	94	79
# 1003	76	83	92
# 1004	63	87	72
# 1005	66	94	83
方式二:基于字典
# 创建DataFrame的第二种方式(基于字典)
scores = {
    '语文': [62, 72, 93, 88, 93],
    '数学': [95, 65, 86, 66, 87],
    '英语': [66, 75, 82, 69, 82],
}
df2 = pd.DataFrame(data=scores, index=np.arange(1001, 1006))
df2
#       语文	数学	英语
# 1001	62	95	66
# 1002	72	65	75
# 1003	93	86	82
# 1004	88	66	69
# 1005	93	87	82
方式三:读取CSV文件
# 创建DataFrame的第三种方法(读取CSV文件)
# read_csv函数的几个重要参数:
# ~ delimiter / sep:分隔符(默认是英文的逗号)
# ~ nrows:读取的行数
# ~ skiprows:跳过哪些行
# ~ usecols:指定读取哪些列
# ~ index_col:指定使用哪个列充当行索引
# ~ encoding:编码(解决乱码现象)
df3 = pd.read_csv(
    r'data/2018年北京积分落户数据.csv',
    index_col='id',
    usecols=['id', 'name', 'score'],
    sep=',', 
    nrows=10,
    skiprows=np.arange(1, 11)
)
df3
#	name	score
#id		
#11	张晓燕	115.45
#12	季进	115.29
#13	徐建安	115.25
#14	赵文学	115.21
#15	周鹏	115.13
#16	龚平	114.88
#17	姜秋梅	114.50
#18	潘阳发	114.42
#19	陈阵	113.67
#20	纪晓峰	113.67
方式四:读取Excel文件
# 创建DataFrame的第四种方法(读取Excel文件)
# header ---> 表头在什么位置(列索引)
# sheet_name ---> 指定读取哪个工作表
df4 = pd.read_excel(
    'data/小宝剑大药房(犀浦店)2018年销售数据.xlsx',
    header=1,
    sheet_name='工作表1'
)
方式五:MySQL
# 创建DataFrame的第五种方式(从MySQL数据库读取数据)
import pymysql

conn = pymysql.connect(host='47.104.31.138', port=3306,
                       user='guest', password='Guest.618',
                       database='hrs', charset='utf8mb4')
df5 = pd.read_sql('select dno, dname, dloc from tb_dept', conn)

# 关闭连接
conn.close()
df6 = pd.read_sql(
    sql='select eno, ename, job, mgr, sal, comm, dno from tb_emp', 
    con=conn,
    index_col='eno'
)

MySQL内联两个表
pd.merge(df6, df5, on='dno', how='inner')

DataFrame中单取某一列或者某一行,会得到Series对象

df6['ename']
添加一列

沿1轴计算平均分,再将平均分加入表格
df[‘平均分’]=np.round(df.mean(axis=1),2)
df

df['平均分']=np.round(df.mean(axis=1),2)
将两列合并为一列
# 相同类型才可以合并
df['test']=df['salary'].map(str)+df['education']
查看最高最低分
max_scores = df.max()
min_scores = df.min()
df.loc['最高分']=max_scores
#loc:取行
df.loc['最低分']=min_scores
df
拼接第一行和最后一行
pd.concat([df[:1],df[-1:len(df)]])
添加某行到末尾
df.append(df.iloc[7])
提取某列含有空值的行
data[data['日期'].isnull()]
输出每列缺失值具体行数
for columname in data.columns:
    if data[columname].count()!=len(data):
        loc=data[columname][data[columname].isnull().value==True].index.tolist()
        print('列名:"{}", 第{}行位置有缺失值'.format(columname,loc))
删除所有存在缺失值的行
"""
axis:0-行操作,1-列操作
how:any-只要有空值就删除(默认),all-全部为空值才删除
inplace:False-返回新的数据集(默认),True-在原数据集上操作
"""
data.dropna(axis=0,how='any',inplace=True)

以列名创建一个dataframe(只要列名)
data_df=pd.DataFrame(columns=data.columns)
isin
df = pd.DataFrame({'num_legs':[2,4],'num_wings':[2,0]},index=['falcon','dog'])
#        num_legs  num_wings
#falcon         2          2
#dog            4          0

df.isin({'num_wings':[0,3]})
#        num_legs  num_wings
#falcon     False      False
#dog        False       True
# 打印换手率为--的行
data[data['换手率(%)'].isin['--']]
diff和shift

Series.diff : Compute the difference of two elements in a Series.
DataFrame.diff : Compute the difference of two elements in a DataFrame.
Series.shift : Shift the index by some number of periods.
DataFrame.shift : Shift the index by some number of periods.

s = pd.Series([90, 91, 85])
s.pct_change()
#0         NaN
#1    0.011111
#2   -0.065934
#dtype: float64

df.keys()

获得信息轴

Series的索引,dataframe的列

Series对象
创建Series对象
# 创建Series对象(方法一)
ser1 = pd.Series(data=[420, 380, 520, 520, 230], index=[f'{x}季度' for x in '一二三三四'])
ser1
# 创建Series对象(方法二)
ser2 = pd.Series(data={
    '一季度': 420,
    '二季度': 380,
    '三季度': 520,
    '四季度': 500
})
ser2
Series切片
ser1[1:3]
#二季度    380
#三季度    520
#dtype: int64
ser1['二季度':'三季度']
#二季度    380
#三季度    520
#三季度    520
#dtype: int64
Series索引
花式索引
ser1[['一季度', '四季度', '一季度']]
#一季度    420
#四季度    230
#一季度    420
#dtype: int64
布尔索引
ser1[ser1 > 400]
一些函数
获取值
ser1.values
#array([420, 380, 520, 520, 230], dtype=int64)
获取索引
ser1.index
# Index(['一季度', '二季度', '三季度', '三季度', '四季度'], dtype='object')
有无重复–布尔值

判断数值是否唯一,与索引无关

ser1.is_unique
# False
判断是第一次出现还是重复–布尔数组
ser3.duplicated()
#0    False
#1    False
#2     True
#3    False
#4    False
#dtype: bool
去除重复
ser3.drop_duplicates()
#0     apple
#1    banana
#3    pitaya
#4     peach
#dtype: object

# 设置保留哪一项
# 默认第一个
#last:最后一项
# False:只要重复就一个都不保留
ser3.drop_duplicates(keep='last')
ser3.drop_duplicates(keep=False)
有无空值
ser1.hasnans
# False
判断是否单增
ser1.is_monotonic_increasing
# False
将索引/值从大到小排序
ser1.sort_values(ascending=False)
ser1.sort_index(ascending=False)
获得去重之后的序列
ser3 = pd.Series(data=['apple','banana','apple','pitaya','peach'])
ser3.unique()
# array(['apple', 'banana', 'pitaya', 'peach'], dtype=object)
获得去重之后的序列的元素个数
ser3.nunique()
# 4
统计元素出现次数和降序
ser3.value_counts()
# apple     2
# pitaya    1
# banana    1
# peach     1
# dtype: int64
判断是否为空值
ser3.isnull()
#0    False
#1    False
#2    False
#3    False
#4    False
#dtype: bool

ser3.notnull()
#0    True
#1    True
#2    True
#3    True
#4    True
#dtype: bool
修改值为空
ser3[1]=np.nan
获得dropna(),丢掉空值,剩下的数据
ser3.dropna()
#0     apple
#2     apple
#3    pitaya
#4     peach
#dtype: object
填补空值
# 指定填补
ser3.fillna('orange')
# 用前一个值填充空值
ser3.fillna(method='ffill')
# 用后一个填充空值
ser3.fillna(method='bfill')
指定条件保留或者替换,where和mask
ser4 = pd.Series(np.random.randint(1,100,6))
ser4
#0    75
#1    47
#2     3
#3    84
#4    29
#5     8
#dtype: int32
    

where

满足条件的保留

# where:把满足条件的数据保留,不满足条件的数据替换
# 把小于30的置为空
ser4.where(ser4>30)
#0    75.0
#1    47.0
#2     NaN
#3    84.0
#4     NaN
#5     NaN
#dtype: float64

# 把小于30的替换成30
ser4.where(ser4>30,30)
#0    75
#1    47
#2    30
#3    84
#4    30
#5    30
#dtype: int32

mask

满足条件的替换

# mask:把满足条件的替换,不满足的保留
ser4.mask(ser4>30,30)
#0    30
#1    30
#2     3
#3    30
#4    29
#5     8
#dtype: int32
根据键/位置根据获取值
ser1.at['一季度']
# 420
ser1.iat[0]
# 420
ser1.loc[['一季度','三季度']]
#一季度    420
#三季度    500
#三季度    430
#dtype: int64
ser1.iloc[[0,3,4]]
#一季度    420
#三季度    500
#三季度    430
#dtype: int64
极差众数均值求和
ser1.sum()
#2780
ser1.describe()['mean']
# 397.1428...

# 极差,最大值和最小值的差
np.ptp(ser1)
# 270

# 众数,数组没有这个方法,系列上有
ser1.mode()
#0    500
#dtype: int64

# 只拿第一个众数
ser1.mode()[0]
# 500

map映射
ser5 = pd.Series(['cat','dog',np.nan,'rabbit'])
#0       cat
#1       dog
#2       NaN
#3    rabbit
#dtype: object

# 映射
ser5.map({'cat':'kitty','dog':'puppy'})
#0    kitty
#1    puppy
#2      NaN
#3      NaN
#dtype: object

ser6 = pd.Series(data=['32','89','100','56','47'])
#0     32
#1     89
#2    100
#3     56
#4     47
#dtype: object

ser6.map(int).mean
#0     32
#1     89
#2    100
#3     56
#4     47
#dtype: int64>
apply函数映射
ser6.apply(lambda x:round(int(x)**0.5*10,1))
线性归一化

线性归一化(标准化):把数据转换到0-1之间的值
X ∗ = X i − X m i n X m a x − X m i n X^{*} = \frac{X_i - X_{min}}{X_{max}-X_{min}} X=XmaxXminXiXmin

零均值归一化(中心化)

X ∗ = X i − μ σ X^{*}=\frac{X_i - \mu}{\sigma} X=σXiμ

ser1 = pd.Series(data=[420,380,520,520,230],index=[f'{x}季度'for x in '一二三三四'])
#一季度    420
#二季度    380
#三季度    520
#三季度    520
#四季度    230
#dtype: int64

# 对ser1进行线性归一化
x_min=min(ser1)
x_max=max(ser1)
ser1.apply(lambda x:((x-x_min)/(x_max-x_min)))
#一季度    0.655172
#二季度    0.517241
#三季度    1.000000
#三季度    1.000000
#四季度    0.000000
#dtype: float64

# 对ser1进行零均值归一化
mu,sigma = ser1.mean(),ser1.std()
ser1.apply(lambda x:(x-mu)/sigma)
#一季度    0.050035
#二季度   -0.283530
#三季度    0.883947
#三季度    0.883947
#四季度   -1.534399
#dtype: float64
取最大/小的几个数
ser1.nlargest(3)
#三季度    520
#三季度    520
#一季度    420
#dtype: int64

# 最小的两个
ser1.nsmallest(2)
#四季度    230
#二季度    380
#dtype: int64
分组\求和
ser1.groupby(level=0).sum()
#一季度     420
#三季度    1040
#二季度     380
#四季度     230
#dtype: int64
转换数据类型
df['salary'].astype(np.float64)
大于某个数的次数
len(df[df['salary']>10000])
数据滑动窗口rolling

移动窗口就是窗口向一端滑行,默认是从右往左,每次滑行并不是区间整块的滑行,而是一个单位一个单位的滑行。

首先我们设置的窗口window=3,也就是3个数取一个均值。index 0,1 为NaN,是因为它们前面都不够3个数,等到index2 的时候,它的值是怎么算的呢,就是(index0+index1+index2 )/3
index3 的值就是( index1+index2+index3)/ 3

作用:5日均线,10日均线…

data['收盘价(元)'].rolling(5).sum()
# 将收盘价5日均线、20日均线与原始数据绘制在同一个图上
data['收盘价(元)'].plot()
data['收盘价(元)'].rolling(5).mean().plot()
data['收盘价(元)'].rolling(20).mean().plot()
resample

对原样本重新处理,对一个常规时间序列数据重新采样和频率转换

# 绘制重新采样数据和原始数据
data['收盘价(元)'].plot()
data['收盘价(元)'].resample('7D').max().plot()
# 'M','A','Q','W': right,在降采样时,各时间段的哪一段是闭合的
时间日期的高效操作:dt.

xx.dt.strftime() – 处理后的数据就不再是datetime类型

xx.dt.strftime(各种格式) :

xx.dt.strftime(’%y-%m-%d’)

xx.dt.strftime(’%y%m%d’)

xx.dt.year – 直接提取出年份–返回int64

xx.dt.month

xx.dt.day

xx.dt.time

xx.dt.hour

xx.dt.weekday – 星期一0,星期天6

df[‘日期一’]-df[‘日期二’] – 返回timedelta64[ns]

df[‘日期一’]-df[‘日期二’].dt.days – 返回Int

计算日期位于某一年的第几天:

df[‘日期一’].dt.dayofyear – 返回int64

计算日期位于一年的第几周

df[‘日期一’].dt.weekofyear – int64

波士顿房价预测求最小二乘解
from sklearn.datasets import load_boston
# 加载波士顿房价数据
dataset = load_boston()
dir(dataset)
# ['DESCR', 'data', 'feature_names', 'filename', 'target']
# 显示数据的描述
print(dataset.DESCR)
# 特征的名字
dataset.feature_names
# array(['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD','TAX', 'PTRATIO', 'B', 'LSTAT'], #dtype='<U7')
# 创建DataFrame对象
df = pd.DataFrame(data=dataset.data, columns=dataset.feature_names)
df['PRICE'] = dataset.target
df
#CRIM	ZN	INDUS	CHAS	NOX	RM	AGE	DIS	RAD	TAX	PTRATIO	B	LSTAT	PRICE
#0	0.00632	18.0	2.31	0.0	0.538	6.575	65.2	4.0900	1.0	296.0	15.3	396.90	4.98	24.0
#1	0.02731	0.0	7.07	0.0	0.469	6.421	78.9	4.9671	2.0	242.0	17.8	396.90	9.14	21.6
# 计算皮尔逊相关系数
df.corr()
# 基于RM(平均房间数)来预测PRICE(房价)
x, y = df['RM'], df['PRICE']
history_data = list(zip(x, y))
import heapq
# k近邻算法(kNN - k Nearest Neighbors)
def predict_price_by_knn(history_data, rm, top_n=5):
    # nearest_data = sorted(history_data, key=lambda x: (x[0] - rm) ** 2)[:top_n]
    nearest_data = heapq.nsmallest(top_n, history_data, key=lambda x: (x[0] - rm) ** 2)
    return np.mean([x[1] for x in nearest_data])
# 预测房价
predict_price_by_knn(history_data, 5.25)
# 12.1
predict_price_by_knn(history_data, 6.125)
# 22.119999
import random

best_a, best_b = None, None
best_mse = np.inf

# 蒙特卡洛模拟
for _ in range(1000):
    a, b = random.randrange(-100, 101), random.randrange(-100, 101)
    curr_mse = np.mean(((a * x + b) - y) ** 2)
    if curr_mse < best_mse:
        best_mse = curr_mse
        best_a, best_b = a, b
print(best_a, best_b)
print(best_mse)
# 9 -33
#44.66433044861659
# 绘制拟合曲线
y_hat = 9 * x - 33
plt.scatter(x, y)
plt.plot(x, y_hat, color='red')
plt.show()
# 用回归模型进行预测
def find_price_by_regression(rm):
    return 9 * rm - 33
# 预测
find_price_by_regression(5.25)

y ^ = a x + b \hat{y} = ax + b y^=ax+b

M S E = 1 N ∑ i = 1 N ( y i ^ − y i ) 2 MSE = \frac{1}{N} \sum_{i=1}^{N}(\hat{y_i} - y_i)^2 MSE=N1i=1N(yi^yi)2

M S E = 1 N ∑ i = 1 N ( a x i + b − y i ) 2 MSE = \frac{1}{N} \sum_{i=1}^{N}(ax_i + b - y_i)^2 MSE=N1i=1N(axi+byi)2

# 求最小二乘解
param1 = np.stack((x, np.ones(x.size))).T
param2 = y
np.linalg.lstsq(param1, param2, rcond=None)
#(array([  9.10210898, -34.67062078]),
# array([22061.87919621]),
# 2,
# array([143.99484122,   2.46656609]))
# 用最小二乘解的结果修正刚才回归模型
def find_price_by_regression(rm):
    return 9.1021 * rm - 34.6706
find_price_by_regression(5.25)
# 13.1154...

plt

matplotlib 是为 python 提供强大绘图功能的第三方库,它的配置文件即 .rc 文件,为 matplotlib 输出图形的几乎所有属性指定了永久的默认值。(图形属性包括包括窗体大小、每英寸的点数、线条宽度、颜色、样式、坐标轴、坐标和网络属性、文本、字体等)

plt的参数字典

通过参数字典rcParams可以访问并修改已经加载的配置项

# 使图表上的中文编码正常显示
# sans-serif:无衬线字体
# 字体详情看C:\Users\Administrator\.matplotlib\fontList.json
plt.rcParams['font.sans-serif']=['STFangsong','STZhongsong']
# 使坐标刻度正常显示正负号
plt.rcParams['axes.unicode_minus']=False

dpi:dot per inch svg 矢量图

plt的图片输出设置
# 设置图片的输出格式为svg,保证图片不糊
%config InlineBackend.figure_format='svg'
图片绘制过程

绘制画布(figsize)->定制刻度->定制位置->保存->显示

# 创建画布
plt.figure(figsize=(8,3),dpi=125)
# 创建坐标系
#创建两个图,目前是第一个图
plt.subplot(2,1,1)
# 指定坐标轴
x = np.linspace(-2*np.pi,2*np.pi,100)   # 等差数列
y1 = np.sin(x)
y2 = np.cos(x)
plt.plot(x,y1,color='red',marker=' ')
# 创建两个图,现在是第二个图
plt.subplot(2,1,2)
plt.plot(x,y2,color='red',marker=' ')
# 支持数学公式的显示
plt.xlabel(r'$ \alpha $')
plt.ylabel(r'$ \sqrt[3]{\frac{1}{sin(\gamma)}} $',rotation=0)
# 保存图片
plt.savefig('dome.svg')
# 显示
plt.show()
# 在图片显示的时候会释放,所以一定要先保存再显示
读取图片
# 读取图片的三维数组
guido = plt.imread('guido.jpg')
guido.shape
# (750,500,3)

# 显示图片
plt.imshow(guido)

# 图片垂直翻转
plt.imshow(guido[::-1])

# 图片镜转
plt.imshow(guido[:, ::-1])

# 图片切割
plt.imshow(guido[30:350, 85:310])

# 图片旋转90
plt.imshow(guido.swapaxes(0, 1))

# 图片变色
plt.imshow(guido[:, :, ::-1])

# 图片马赛克
plt.imshow(guido[::12, ::12])

数据分析的基本流程

数据分析的一般性流程:

  1. 提取数据(筛选)

    • 布尔索引

    • query():相当于布尔索引

    • drop():删除指定的行或列

      df.drop(columns=['categories'], axis=1,inplace=True)
      # axis : {0 or 'index', 1 or 'columns'}, default 0
      # Whether to drop labels from the index (0 or 'index') or   columns (1 or 'columns').
      del df['categories']
      
    • rename():重命名行或列索引

    • reset_index():重置索引(将索引变成普通列)

    • set_index():设置索引(将一个列或多个列指定为索引)

    • reindex():调整行或列索引的顺序(还可以做花式索引)

  2. 整合和重塑

    • merge():实现两个DataFrame对象的内连接或外连接(适用于两张表有关联)。
    • concat():将两个或多个DataFrame的数据拼接到一起(适用于多张表结构一致)。
  3. 数据清洗(缺失值、重复值、异常值)

    • 缺失值:

      • dropna():删除空值

      • fillna():填充空值 <— 均值、中位数、众数

      • isnull() / notnull():判断空值

        # 检查数据中是否含有任何缺失值
        df.isnull().values.any()
        
    • 重复值:

      • duplicated():判断是否重复
      • drop_duplicates():删除重复值
    • 异常值:

      • 异常值 / 极端值 / 离群值 —> 1.5*IQR / 三西格玛法则
      • drop():删除
      • replace():替换
    • 预处理:

      • apply():将函数作用到数据上,需要指定在哪个轴上执行
      • transform():将(多个)函数作用到数据上
      • applymap():对每个元素使用指定的函数进行映射,相当于Series上的map()方法 —> elementwise

    注意:这三个方法都是高阶函数的用法,apply()方法接受的函数,可以是带归约性质的函数(聚合函数),也可以是没有归约性质的函数。transform()方法接受的函数不能是带归约功能的函数,只能是对数据做变换的函数,简单的说就是函数的执行不会减少数据的量。

  4. 数据透视(排序、分组、聚合)

    • 透视表:根据A统计B(根据A列对数据进行分组,再对B列上聚合函数)
    • groupby() —> sum() / mean() / max() / …
    • pivot_table() —> 专门生成透视表的函数
    • cut() —> 数据离散化(分箱)
  5. 数据可视化(统计图表)

    • plot() —> 出统计图表
      • figsize:图表的尺寸
      • kind:图表的类型(line、bar、barh、pie、scatter、box、hist)
    • 定制图表:使用matplotlib中的函数
      • xticks():横坐标的刻度
      • yticks():纵坐标的刻度
      • xlabel():横轴的标签
      • ylabel():纵轴的标签
      • legend():图例
      • grid():网格线
      • text():在图上添加文字
      • annotate():在图上添加标记

布尔索引

- `query()`:相当于布尔索引

- `drop()`:删除指定的行或列

  ```python
  df.drop(columns=['categories'], axis=1,inplace=True)
  # axis : {0 or 'index', 1 or 'columns'}, default 0
  # Whether to drop labels from the index (0 or 'index') or   columns (1 or 'columns').
  del df['categories']
  ```

- `rename()`:重命名行或列索引

- `reset_index()`:重置索引(将索引变成普通列)

- `set_index()`:设置索引(将一个列或多个列指定为索引)

- `reindex()`:调整行或列索引的顺序(还可以做花式索引)
  1. 整合和重塑

    • merge():实现两个DataFrame对象的内连接或外连接(适用于两张表有关联)。
    • concat():将两个或多个DataFrame的数据拼接到一起(适用于多张表结构一致)。
  2. 数据清洗(缺失值、重复值、异常值)

    • 缺失值:

      • dropna():删除空值

      • fillna():填充空值 <— 均值、中位数、众数

      • isnull() / notnull():判断空值

        # 检查数据中是否含有任何缺失值
        df.isnull().values.any()
        
    • 重复值:

      • duplicated():判断是否重复
      • drop_duplicates():删除重复值
    • 异常值:

      • 异常值 / 极端值 / 离群值 —> 1.5*IQR / 三西格玛法则
      • drop():删除
      • replace():替换
    • 预处理:

      • apply():将函数作用到数据上,需要指定在哪个轴上执行
      • transform():将(多个)函数作用到数据上
      • applymap():对每个元素使用指定的函数进行映射,相当于Series上的map()方法 —> elementwise

    注意:这三个方法都是高阶函数的用法,apply()方法接受的函数,可以是带归约性质的函数(聚合函数),也可以是没有归约性质的函数。transform()方法接受的函数不能是带归约功能的函数,只能是对数据做变换的函数,简单的说就是函数的执行不会减少数据的量。

  3. 数据透视(排序、分组、聚合)

    • 透视表:根据A统计B(根据A列对数据进行分组,再对B列上聚合函数)
    • groupby() —> sum() / mean() / max() / …
    • pivot_table() —> 专门生成透视表的函数
    • cut() —> 数据离散化(分箱)
  4. 数据可视化(统计图表)

    • plot() —> 出统计图表
      • figsize:图表的尺寸
      • kind:图表的类型(line、bar、barh、pie、scatter、box、hist)
    • 定制图表:使用matplotlib中的函数
      • xticks():横坐标的刻度
      • yticks():纵坐标的刻度
      • xlabel():横轴的标签
      • ylabel():纵轴的标签
      • legend():图例
      • grid():网格线
      • text():在图上添加文字
      • annotate():在图上添加标记
  5. 数据的洞察(发现问题给出建议)—> 一般性的方法(数据思维)和经验的积累

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值