# 数学建模导论 --- pandas的简单使用 --2025年2月学习笔记 学习内容摘自[intro-mathmodel-课程详情 | Datawhale](https://www.datawhale.cn/learn/content/85/3136) 感谢分享! 学习数学建模更重要的是知道面对不同体量的数据如何快速下手找准最合适的建模方法 靠山吃山,靠水吃水。小数据有小数据的方法,大数据有大数据的方法。拿着线性回归的那一套去拟合大数据,往往效果不会很好;拿着神经网络去学小数据,学到的东西往往没有意义(我们会笑称:你是想去研究few-shot learning吗)。面对不同的数据,能够使用最合适的方法最为重要。而判断此方法是否合适,一个根本的衡量就是数据的体量。 ## 数据科学的研究对象 - 数据的获取和存储:包括爬虫、软件定义存储、硬件存储有关背景知识等。 - 数据的处理:包括分布式计算、并行计算、数据流等知识,以及Hadoop、Spark等大数据框架。 - 数据的分析:包括统计学、数据挖掘与机器学习、计算机视觉、自然语言处理等内容,重在挖掘数据中的模式与知识。 - 数据的管理:现代数据库系统及其架构等内容。 - 数据的应用:数据可视化、数据相关软件的开发、报表分析以及如何将数据挖掘得到的结果还原为实际问题的解决方案。 ## 使用pandas处理数据的基础 ### 1.环境相关 这里我使用的是pycharm进行的python编写,环境是用conda建的新环境 python = 3.10 ```bash # 使用清华镜像源下载更快 pip insatll pandas -i https://pypi.tuna.tsinghua,edu.cn/simple ``` ### 2.练习一 ```python import pandas as pd import numpy as np # 字典格式 data = {'animal': ['cat', 'cat', 'snake', 'dog', 'dog', 'cat', 'snake', 'cat', 'dog', 'dog'], 'age': [2.5, 3, 0.5, np.nan, 5, 2, 4.5, np.nan, 7, 3], 'visits': [1, 3, 2, 3, 2, 3, 1, 1, 2, 1], 'priority': ['yes', 'yes', 'no', 'yes', 'no', 'no', 'no', 'yes', 'no', 'no']} # 作为索引 labels = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] df = pd.DataFrame(data) # 默认输出有数值的统计结果 print(df.describe()) # age visits # count 8.000000 10.000000 # mean 3.437500 1.900000 # std 2.007797 0.875595 # min 0.500000 1.000000 # 25% 2.375000 1.000000 # 50% 3.000000 2.000000 # 75% 4.625000 2.750000 # max 7.000000 3.000000 # 计数,均值,标准差,最小值。。。。最大值 # 增加参数即可输出上面的animal和priority print(df.describe(include='all')) # animal age visits priority # count 10 8.000000 10.000000 10 # unique 3 NaN NaN 2 # top cat NaN NaN no # freq 4 NaN NaN 6 # mean NaN 3.437500 1.900000 NaN # std NaN 2.007797 0.875595 NaN # min NaN 0.500000 1.000000 NaN # 25% NaN 2.375000 1.000000 NaN # 50% NaN 3.000000 2.000000 NaN # 75% NaN 4.625000 2.750000 NaN # max NaN 7.000000 3.000000 NaN # 输出为数据的前五列 print(df.head(5)) # animal age visits priority # 0 cat 2.5 1 yes # 1 cat 3.0 3 yes # 2 snake 0.5 2 no # 3 dog NaN 3 yes # 4 dog 5.0 2 no # 除了最后一列不输出 print(df.head(-1)) # animal age visits priority # 0 cat 2.5 1 yes # 1 cat 3.0 3 yes # 2 snake 0.5 2 no # 3 dog NaN 3 yes # 4 dog 5.0 2 no # 5 cat 2.0 3 no # 6 snake 4.5 1 no # 7 cat NaN 1 yes # 8 dog 7.0 2 no # 打印有动物和age的列 print(df[['animal', 'age']]) # animal age # 0 cat 2.5 # 1 cat 3.0 # 2 snake 0.5 # 3 dog NaN # 4 dog 5.0 # 5 cat 2.0 # 6 snake 4.5 # 7 cat NaN # 8 dog 7.0 # 9 dog 3.0 # 全部打印 print(df) # animal age visits priority # 0 cat 2.5 1 yes # 1 cat 3.0 3 yes # 2 snake 0.5 2 no # 3 dog NaN 3 yes # 4 dog 5.0 2 no # 5 cat 2.0 3 no # 6 snake 4.5 1 no # 7 cat NaN 1 yes # 8 dog 7.0 2 no # 9 dog 3.0 1 no # 输出3 4 8行的 animal age print(df.loc[[3, 4, 8], ['animal', 'age']]) # animal age # 3 dog NaN # 4 dog 5.0 # 8 dog 7.0 # 输出原有index print(df.index) # RangeIndex(start=0, stop=10, step=1) # 更改index df.index = labels print(df.index) # Index(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'], dtype='object') print('改变之前:', df.loc[['f'],['age']]) df.loc[['f'], ['age']] = 1.5 print('改变之后:', df.loc[['f'],['age']]) # 改变之前: age # f 2.0 # 改变之后 age # f 1.5 # 求和 print(df['visits'].sum()) # 19 print(df.groupby(['animal'])['age'].mean()) # animal # cat 2.333333 # dog 5.000000 # snake 2.500000 # Name: age, dtype: float64 ``` ### 3.练习二 ```python import numpy as np import pandas as pd df = pd.DataFrame({'From_To': ['LoNDon_paris', 'MAdrid_miLAN', 'londON_StockhOlm', 'Budapest_PaRis', 'Brussels_londOn'], 'FlightNumber': [10045, np.nan, 10065, np.nan, 10085], 'RecentDelays': [[23, 47], [], [24, 43, 87], [13], [67, 32]], 'Airline': ['KLM(!)', ' (12)', '(British Airways. )', '12. Air France', '"Swiss Air"']}) # 打印出来看看df print(df) # From_To FlightNumber RecentDelays Airline # 0 LoNDon_paris 10045.0 [23, 47] KLM(!) # 1 MAdrid_miLAN NaN [] (12) # 2 londON_StockhOlm 10065.0 [24, 43, 87] (British Airways. ) # 3 Budapest_PaRis NaN [13] 12. Air France # 4 Brussels_londOn 10085.0 [67, 32] "Swiss Air" # 缺失值常用nan表示 # interpolate() 是 Pandas 中的一个方法,用于填补缺失值(NaN)。 # 它使用插值算法推测缺失数据点的值,默认情况下采用线性插值(通过连接已知点的直线来估算未知点的值) # astype(int) 方法将插值后的 FlightNumber 列的所有数据转换为整数类型。 df['FlightNumber'] = df['FlightNumber'].interpolate().astype(int) print(df) # From_To FlightNumber RecentDelays Airline # 0 LoNDon_paris 10045 [23, 47] KLM(!) # 1 MAdrid_miLAN 10055 [] (12) # 2 londON_StockhOlm 10065 [24, 43, 87] (British Airways. ) # 3 Budapest_PaRis 10075 [13] 12. Air France # 4 Brussels_londOn 10085 [67, 32] "Swiss Air" temp = df['From_To'].str.split("_", expand=True) print(temp) # 0 1 # 0 LoNDon paris # 1 MAdrid miLAN # 2 londON StockhOlm # 3 Budapest PaRis # 4 Brussels londOn # 赋予为列From与To temp.columns = ['From', 'To'] print(temp) # From To # 0 LoNDon paris # 1 MAdrid miLAN # 2 londON StockhOlm # 3 Budapest PaRis # 4 Brussels londOn # 处理首字母 temp['From'] = temp['From'].str.capitalize() temp['To'] = temp['To'].str.capitalize() print(temp) # From To # 0 London Paris # 1 Madrid Milan # 2 London Stockholm # 3 Budapest Paris # 4 Brussels London # 'From_To': # 这是你想要删除的列名。在这个例子中,删除的是名为 'From_To' 的列。 # axis 参数指定是删除行还是列: # axis=0 表示删除行(按行操作)。 # axis=1 表示删除列(按列操作)。 # inplace=True 表示直接在原数据框 df 上进行操作,修改会生效并且不会返回新的数据框。即,不需要重新赋值给 df,它会在原地修改数据框。 # 如果设置为 inplace=False(默认值),则 drop() 方法会返回一个删除指定列的新的数据框,而原数据框 df 不会被改变。 df.drop('From_To', axis=1, inplace=True) print(df) # FlightNumber RecentDelays Airline # 0 10045 [23, 47] KLM(!) # 1 10055 [] (12) # 2 10065 [24, 43, 87] (British Airways. ) # 3 10075 [13] 12. Air France # 4 10085 [67, 32] "Swiss Air" # 合并到df df[['From', 'To']] = temp print(df) # FlightNumber RecentDelays Airline From To # 0 10045 [23, 47] KLM(!) London Paris # 1 10055 [] (12) Madrid Milan # 2 10065 [24, 43, 87] (British Airways. ) London Stockholm # 3 10075 [13] 12. Air France Budapest Paris # 4 10085 [67, 32] "Swiss Air" Brussels London # 去除其他字符 # r'([a-zA-Z\s]+)' 是正则表达式,用于匹配字符串中连续的字母和空格 # strip()去除首位空格,但是中间不管 df['Airline'] = df['Airline'].str.extract(r'([a-zA-Z\s]+)', expand=False).str.strip() print(df) # FlightNumber RecentDelays Airline From To # 0 10045 [23, 47] KLM London Paris # 1 10055 [] Air France Madrid Milan # 2 10065 [24, 43, 87] British Airways London Stockholm # 3 10075 [13] Air France Budapest Paris # 4 10085 [67, 32] Swiss Air Brussels London delays = df['RecentDelays'].apply(pd.Series) print(delays) # 0 1 2 # 0 23.0 47.0 NaN # 1 NaN NaN NaN # 2 24.0 43.0 87.0 # 3 13.0 NaN NaN # 4 67.0 32.0 NaN delays.columns = ['delay_%s' % i for i in range(1, len(delays.columns)+1)] df = df.drop('RecentDelays', axis=1).join(delays, how='left') # join(delays, how='left')的主体似乎是dalays来区分左右 print(df) # FlightNumber Airline From ... delay_1 delay_2 delay_3 # 0 10045 KLM London ... 23.0 47.0 NaN # 1 10055 Air France Madrid ... NaN NaN NaN # 2 10065 British Airways London ... 24.0 43.0 87.0 # 3 10075 Air France Budapest ... 13.0 NaN NaN # 4 10085 Swiss Air Brussels ... 67.0 32.0 NaN # 将delay_i列的控制nan都填为自身的平均值。fillna自如其意 for i in range(1, 4): df[f'delay_{i}'] = df[f'delay_{i}'].fillna(np.mean(df[f'delay_{i}'])) print(df) # FlightNumber Airline From ... delay_1 delay_2 delay_3 # 0 10045 KLM London ... 23.00 47.000000 87.0 # 1 10055 Air France Madrid ... 31.75 40.666667 87.0 # 2 10065 British Airways London ... 24.00 43.000000 87.0 # 3 10075 Air France Budapest ... 13.00 40.666667 87.0 # 4 10085 Swiss Air Brussels ... 67.00 32.000000 87.0 # 增加一行相同的 df = df._append(df.loc[df['FlightNumber'] == 10085, :], ignore_index=True) print(df) # [5 rows x 7 columns] # FlightNumber Airline From ... delay_1 delay_2 delay_3 # 0 10045 KLM London ... 23.00 47.000000 87.0 # 1 10055 Air France Madrid ... 31.75 40.666667 87.0 # 2 10065 British Airways London ... 24.00 43.000000 87.0 # 3 10075 Air France Budapest ... 13.00 40.666667 87.0 # 4 10085 Swiss Air Brussels ... 67.00 32.000000 87.0 # 5 10085 Swiss Air Brussels ... 67.00 32.000000 87.0 # 去重 df = df.drop_duplicates() print(df) # FlightNumber Airline From ... delay_1 delay_2 delay_3 # 0 10045 KLM London ... 23.00 47.000000 87.0 # 1 10055 Air France Madrid ... 31.75 40.666667 87.0 # 2 10065 British Airways London ... 24.00 43.000000 87.0 # 3 10075 Air France Budapest ... 13.00 40.666667 87.0 # 4 10085 Swiss Air Brussels ... 67.00 32.000000 87.0 ``` ## 数据的规约 min-max规约的表达式形如: $$ x_n=\frac {max(x)−min(x)}{x−min(x)} $$ 这一操作的目的是为了消除量纲影响,所有的属性都被规约到[0,1]的范围内,数据的偏差不会那么大。但是如果出现异常值,比如非常大的数值,那么这个数据的分布是有偏的。为了对数据的分布进行规约,还会使用到另一个常用的方法就是Z-score规约: $$ x_n=\frac {x− \bar x}{std(x)} $$ 本质上,一列数据减去其均值再除以标准差,如果这一列数据近似服从正态分布,这个过程就是化为标准正态分布的过程。Z-score规约和min-max规约往往不是二者取其一,有时候两个可以组合起来用。 ## 初探数据分析 ### 分析方法 回归分析: ​ 对数据进行线性回归,看相关度的系数即可分析 分类分析: ​ 逻辑分类法 ### 假设检验 ​ 正态性检验:于参数检验比非参数检验更灵敏,因此一旦数据是正态分布的,我们应该使用参数检验,三种方法判断数据的正态性:可视化判断-正态分布概率图;Shapiro-Wilk检验;D'Agostino's K-squared检验。 画图:分位数一一对应即可 1)单组样本均值假定的检验 ​ 单样本t检验与wilcoxon检验。 2)两组样本的均值相等性检验 若两个样本的总体都服从正态分布,那么我们可以使用双样本t检验。如果不服从正态分布,则可以使用Mannwhitneyu秩和检验,Mannwhitneyu秩和检验是一种非参数检验。 先进行两组数据的方差齐性检验 若在显著性水平0.05下,不能拒绝两组样本方差相等的假设(p=0.8277),因此需要使用方差相等的t检验 在进行两组间均值比较的时候,有一种特殊情况——两个样本“故意”不独立。这种情况多出现两个样本分别为同一个受试个体不同时间的受试结果,这两个样本是“成对”的,是彼此紧密相连的。对这样两个样本进行均值比较检验,就是成对检验