数据科学
参考资料
Data 100 Fall 2022 website 公开课
Data 100
lec 01
-
读取压缩文件内容
1 2 3 4 5 6with zipfile.ZipFile(filename, "r") as zf: # 列出压缩包内的文件 print(zf.filelist) f = '想要打开的文件名' with zf.open(f) as fp: ...... -
透视表
1 2 3 4 5 6 7 8 9 10( pd.pivot_table( babynames[babynames['Name'].isin(subset_names)], index=['Name', 'Year'], columns='Sex', values='Count', aggfunc='sum', fill_value=0, margins=True ) .drop(labels='All', level=0, axis=0) # 删除汇总行 .rename_axis(None, axis=1) # 去掉列索引名 # .reset_index() # 把 (Name, Year) 从索引挪回列 )如上, 对于过长的链式代码可以用括号包裹并分行书写, 提高可读性
-
判断值是否在某个列表中
1pd.Series.isin([]) -
添加一新列 col
1 2 3 4.assign(col=lambda row: ...) # 例如: .assign(Propf=lambda row: row.F / (row.F + row.M)) -
好用的筛选方法
1.query("@propf_min < Propf < @propf_max & Year > @year_thresh & Name != 'All'")其中 @变量名 表示引用在查询上下文中 已定义的变量
lec 02
-
判断值是否以某字符开头
1elections["Party"].str.startswith("A") -
字段值去重
1elections["Party"].unique() -
对字段值进行排序
1 2 3elections["Candidate"].sort_values(ascending = ) # 或者 elections.sort_values("Candidate", ascending = )
lec 03
有时候 DataFrame 不在单元格的最后一行, 可以使用 display, 优化展示结构
|
|
-
按照某一列分组, 然后取出 每一组的第一个数据
1.groupby(colname).first()例如
1 2 3 4 5 6 7 8 9 10import pandas as pd a = pd.DataFrame( data={ 'col1': ['a', 'a', 'a', 'b', 'c'], 'col2': [133, 20, 30, 22, 33], } ) display(a) a.groupby('col1').first()
lec 04
-
设置输出格式, 例如行列数、精度等等
1 2 3 4 5 6np.set_printoptions(threshold=20, precision=2, suppress=True) pd.set_option('display.max_rows', 30) pd.set_option('display.max_columns', None) pd.set_option('display.precision', 2) pd.set_option('display.float_format', '{:.2f}'.format)
lec 05
-
将某一列转换成字符串, 再进行文本替换
1Series.str.replace -
根据参数 col 指定某一个特征类别绘制多个图像, 并放在一起便于对比
1sns.catplot() -
对列名进行重命名, 注意需要传入参数名
columns1 2 3 4tmp = {0: "Day", 1: "Month", 2: "Year", 3: "Hour", 4: "Minute", 5: "Second", 6: "Time Zone"} b = b.rename(columns=tmp) # 或者 b.columns = ['...', '...', '...']
宽表转长表
|
|
one-hot 是将分类变量的每一个可能取值,都变成一个独立的 0/1 特征列, 即宽表
列数可能随属性值的增加而爆炸
而 pd.melt 更准确的语义是“把宽格式转为长格式”
-
宽表(Wide Format)例子
各城市的年度销售额
城市 2023 2024 2025 北京 100 120 140 上海 150 170 190 -
长表(Long Format)例子
城市 年份 销售额 北京 2023 100 北京 2024 120 北京 2025 140 上海 2023 150 上海 2024 170 上海 2025 190 行数变多,重复信息多
-
转换
-
宽 → 长(熔接 / melt)
把多列“压扁”成一列变量名和一列变量值,其余标识列重复
关键操作:指定哪些列是“标识符”(不变),哪些列是“被熔化的列”(变成变量+值) -
长 → 宽(透视 / pivot)
把变量列中的不同取值展开成多列,值列填充到对应的单元格
关键操作:指定索引列、列名列、值列
-
可以把宽表看作“二维矩阵”,长表看作“三元组列表”
lec 06
-
找到一组数的分位数值
1 2 3 4 5 6import numpy as np np.percentile(a, q) # q 是 0~100 之间的浮点数,表示百分位数 # 用法解释:https://blog.csdn.net/qq_16488989/article/details/116295686 -
熟悉常用的绘图函数
1 2 3 4 5 6 7 8 9 10 11import seaborn as sns sns.countplot() sns.histplot() sns.rugplot() sns.boxplot() sns.violinplot() sns.scatterplot() sns.jointplot() sns.lmplot()
lec 07
-
DataFrame 替换
1 2 3 4 5 6 7 8 9 10 11df.replace({ 'educ': { 1: "<HS", 2: "HS", 3: "<BA", 4: "BA", 5: ">BA" } }) # 或者 df['educ'].replace( [1, 2, 3, 4, 5], ["<HS", "HS", "<BA", "BA", ">BA"] ) -
设置某列为索引并按照某列分组
1 2 3 4 5b = a.set_index("???").groupby("Gender") b.get_group("Men").drop("Gender", axis=1) b.get_group("Women").drop("Gender", axis="columns") -
绘制曲线时, 可以循环使用曲线类型和颜色
1 2 3 4 5 6 7 8 9 10 11import matplotlib.pyplot as plt from cycler import cycler linestyles = ['-', '--', ':', '-.' ] colors = plt.cm.Dark2.colors lines_c = cycler('linestyle', linestyles) color_c = cycler('color', colors) ax.set_prop_cycle(color_c * lines_c) -
若条件
cond <= a/2则返回1/a,否则返回01 2 3 4 5np.piecewise( x, [cond <= a/2, cond > a/2], [1/a, 0] )其中 cond 通常是关于 x 的表达式
lec 08
-
设置 sns 绘图格式
1 2 3 4 5 6 7sns.set_theme( style = 'darkgrid', font_scale = 1.5, rc = { 'figure.figsize':(7,5) } ) -
先分组聚合再重置索引
1 2 3???.groupby( ["?", "??"] ).agg("mean").reset_index() -
随机采样中的 replace 参数
1.sample(1000, replace = False)replace = False 无放回抽取
replace = True 有放回抽取
lec 11
-
DataFrame 转换成 Numpy 数组
1X = X.to_numpy() -
R 方计算公式、相关系数计算公式
在简单线性回归中,R 方(决定系数)与相关系数 r 的关系为:R² = r²
其中 r 是 y 与 x 之间的皮尔逊相关系数
lec 16
-
在图中添加子图
1 2 3 4 5 6 7 8 9 10 11 12fig, ax = plt.subplots() ax.plot( "fraction_date", "data_filled", "r.", data=data ) # 子图 axin1 = ax.inset_axes([0.1, 0.5, 0.35, 0.35]) axin1.plot(data2, "b") -
获取当前文件的绝对路径
1 2 3 4 5 6from pathlib import Path myfile = './lec16.ipynb' DATA_DIR = Path(myfile).absolute() DATA_DIR -
获取 home 目录路径再拼接其他路径
1Path.home().joinpath('python', 'scripts', 'test.py') -
读取 xarray 数据文件
1 2 3 4import xarray as xr ds = xr.open_dataset('xarray文件名') -
绘制俯视地球仪视角的图像
1 2 3 4 5 6 7 8 9 10 11import cartopy.crs as ccrs pfit = data['...'].polyfit("time", 1) p = pfit.polyfit_coefficients[0].plot( subplot_kws=dict(projection=ccrs.Orthographic(0, 90), facecolor="gray"), transform=ccrs.PlateCarree(central_longitude=0), cbar_kwargs={"label": "trend deg/yr"}, ) p.axes.coastlines();
lec 17
直方图 hist
条形图、柱状图 barh
通过 随机抽样 来模拟 数据的分布 情况
lec 19
-
绘制散点图并进行拟合
1sns.lmplot(data = )
lec 20
-
将筛选出的内容保存到变量 var 中
1 2 3 4%%sql var << 这里是一些MySQL的筛选语句。。。 display(var) -
执行筛选语句
1 2 3 4 5 6 7 8 9query = """ SQL 的筛选语句 """ %%sql {query} # 或者 %%sql $query -
缩短 IPython 的错误显示
1%xmode minimal
lec 21
|
|
-
一种文件路径写法, 注意斜杠后面加字符串
1 2 3 4 5 6from pathlib import Path df = pd.read_csv( Path.home() / "shared/climate-data/monthly_in_situ_co2_mlo_cleaned.csv" ) -
缩写解释
- HDF5:一种灵活的二进制文件格式,可存储分层嵌套的数据,并支持任意数值类型的多维密集数组
- NetCDF:一个数据模型,用于以维度、变量和属性的形式组织数组
- Xarray:一个用于数值计算和数据分析的 Python 库,它将 NetCDF 数据模型暴露为 Python 对象
lec 22
做奇异值分解需要先做数据中心化, 即对于每一列, 我们应该减去该列的平均值
-
分组后查看每个组的数量大小
1 2 3df.groupby( ['member', 'vote'] ).size().to_frame() -
旋转坐标轴, rotation 控制角度
1plt.xticks(..., rotation=90); -
对数据进行采样, 返回迭代器
需要通过 for 循环来赋值
1 2 3# 随机取出 20 个散点 for _, row in mydata.sample(20).iterrows(): a, b = row['...'], row['...']
lec 23
-
生成一个含有 longitude、latitude、geometry 字段的 DataFrame
1 2 3 4 5 6 7 8 9import geopandas as gpd gpd.GeoDataFrame( df, geometry=gpd.points_from_xy( df.longitude, df.latitude ) )
lec 24
-
绘制有 抖动 的图像, 防止散点重叠
1sns.stripplot -
数据分箱 操作
1 2 3 4 5bins = pd.cut() bins[0].left bins[0].right bins[0] -
适应子图之间的填充距离
1plt.tight_layout() -
优化函数
1from scipy.optimize import minimize -
单次观测的交叉熵损失方程
$$ \textrm{loss} = -y \log(\hat{y}) - (1-y)\log(1-\hat{y}) $$
-
逻辑函数
$\sigma(t) = \frac{1}{1 + e^{-t}}$, 其中 $t = \theta_{0}+\theta_{1}x$
- $\theta_{1}$ 绝对值越大, 越不平滑, 即越靠近点 (0,1) 和点 (0,0)
- $\theta_{1}$ 小于 0 形状为 2, 大于 0 形状为 S
-
$这里 ~~~~~ 到这里有空格$
数学公式中的空格在 LaTeX 中使用
~来插入不可见空格
lec 25
字符串前加 r,表示原始字符串,避免反斜杠被转义
相当于双反斜杠的效果
-
展示原始的数据离散分布情况
用于绘制出 一维数组 中 数据点实际的分布位置 情况, 单纯地将记录值在坐标轴上表现出来
1sns.rugplot(); -
去掉图例的标题
1plt.gca().legend_.set_title(None) -
上下翻转 y 轴
1plt.gca().invert_yaxis()
关于 SVD
简介
Singular Value Decomposition(奇异值分解) 是一种提取信息的强大工具。
它将矩阵分解为三个更简单的矩阵,能够揭示数据中隐藏的潜在模式。
主要应用领域:
- 隐性语义分析 (Latent Semantic Analysis, LSA) 或隐性语义索引 (Latent Semantic Indexing, LSI)
- 推荐系统 (Recommender system)
- 矩阵形式数据(主要是图像数据)的压缩
线性变换
在推导 SVD 之前,先回顾一下线性变换。以 $2\times2$ 对角矩阵为例:
$$ M = \begin{bmatrix}3 & 0 \ 0 & 1\end{bmatrix} $$
从集合上讲,M 是将二维平面上的点 (x,y) 经过线性变换到另一个点的 变换矩阵,如下所示:
$$ \begin{bmatrix}3&0\0&1\end{bmatrix}\begin{bmatrix}x \ y\end{bmatrix} = \begin{bmatrix}3x \ y\end{bmatrix} $$
该变换的几何效果是,变换后的平面沿 $x$ 轴方向拉伸 3 倍,$y$ 轴方向不变。
SVD 推导
核心思想:对于任意矩阵 $M$,我们寻找一组原始空间的标准正交基 $\mathbf{v}_i$
使得它们经过 $M$ 变换后得到的向量 $M\mathbf{v}_i$ 仍然两两正交(只是长度可能改变)
假设找到了这样一组正交单位向量 $\mathbf{v}_1, \mathbf{v}_2$(即右奇异向量),使得 $M\mathbf{v}_1$ 与 $M\mathbf{v}_2$ 正交。
记 $\mathbf{u}_1, \mathbf{u}_2$ 分别为它们方向上的单位向量,长度为 $\sigma_1, \sigma_2$:
$$ M\mathbf{v}_1 = \sigma_1 \mathbf{u}_1,\qquad M\mathbf{v}_2 = \sigma_2 \mathbf{u}_2 $$
这里的 $\sigma_1, \sigma_2$ 称为矩阵 $M$ 的奇异值,表示原向量在变换中的拉伸倍数。
任意向量 $\mathbf{x}$ 可用这组正交基展开:
$$ \mathbf{x} = (\mathbf{v}_1\cdot \mathbf{x})\mathbf{v}_1 + (\mathbf{v}_2\cdot \mathbf{x})\mathbf{v}_2 $$
例如,仍取上面 $M=\begin{bmatrix}3&0\0&1\end{bmatrix}$,此时很容易看出 $\mathbf{v}_1=[1,0]^T, \mathbf{v}_2=[0,1]^T$ 就是一组合适的正交基(变换后方向不变,依然正交)。
若 $\mathbf{x}=[3,2]^T$,则 $\mathbf{x} = 3\mathbf{v}_1 + 2\mathbf{v}_2$,于是:
$$ M\mathbf{x} = 3 M\mathbf{v}_1 + 2 M\mathbf{v}_2 = 3\cdot(3\mathbf{u}_1) + 2\cdot(1\mathbf{u}_2)=9\mathbf{u}_1+2\mathbf{u}_2 $$
与直接计算 $M\mathbf{x} = [9,2]^T$ 一致。
回到一般推导,利用线性性质:
$$ M\mathbf{x} = (\mathbf{v}_1\cdot \mathbf{x})M\mathbf{v}_1 + (\mathbf{v}_2\cdot \mathbf{x})M\mathbf{v}_2 $$
$$ M\mathbf{x} = (\mathbf{v}_1^T\mathbf{x})\sigma_1\mathbf{u}_1 + (\mathbf{v}_2^T\mathbf{x})\sigma_2\mathbf{u}_2 $$
将标量 $\mathbf{v}_i^T\mathbf{x}$ 移到后方:
$$ M\mathbf{x} = \mathbf{u}_1\sigma_1(\mathbf{v}_1^T\mathbf{x}) + \mathbf{u}_2\sigma_2(\mathbf{v}_2^T\mathbf{x}) = (\mathbf{u}_1\sigma_1\mathbf{v}_1^T + \mathbf{u}_2\sigma_2\mathbf{v}_2^T),\mathbf{x} $$
两边去掉 $\mathbf{x}$,得到:
$$ M = \mathbf{u}_1\sigma_1\mathbf{v}_1^T + \mathbf{u}_2\sigma_2\mathbf{v}_2^T $$
推广到一般情况,将向量拼成矩阵:
$$ M = U\Sigma V^T $$
其中:
- $U$ 的列是 $\mathbf{u}_1, \mathbf{u}_2, \dots$(左奇异向量,变换后的新正交基);
- $V$ 的列是 $\mathbf{v}_1, \mathbf{v}_2, \dots$(右奇异向量,原始正交基);
- $\Sigma$ 是对角矩阵,对角线元素为奇异值 $\sigma_1 \ge \sigma_2 \ge \dots \ge 0$。
总结:
- 任意矩阵 $M$ 都可分解为 $U\Sigma V^T$。
- $V$ 的列是原始空间的标准正交基,$U$ 的列是变换后的标准正交基。
- $\Sigma$ 的对角元素(奇异值)表示对应方向上拉伸/压缩的比例,且按从大到小排列,值越大代表该维度越重要。
用 SVD 做信息压缩:只保留前 $k$ 个最大的奇异值(及对应的奇异向量),即可用较少的维度近似原始矩阵。常用启发式策略:
-
直接指定保留前 $k$ 个奇异值;
-
保留一定百分比的“能量”(例如 90%),先计算所有奇异值的平方和,然后依次累加直到占比超过阈值:
$$ k = \min_k\ \left{ \frac{\sum_{i=1}^{k}\sigma_i^2}{\sum_{i=1}^{r}\sigma_i^2} \ge 0.9 \right} $$
其中 $r$ 为矩阵的秩(非零奇异值的个数)。
代码示例
|
|