疫情前后机票价格的季节性研究

—— 基于巴西航空市场的实证分析 ——

作者: Alessandro V. M. Oliveira

数据来源:巴西民航局(ANAC) 2013-2023年

一、研究背景与意义

1. 价格季节性定义

指商品/服务价格因季节、假期、消费习惯、天气等供需因素,在全年呈现周期性波动的现象。

例如航空业中,假期出行需求激增会推高票价,淡季需求低迷则促使票价下降。

2. 航空业季节性的全链条影响

对航司

通过季节性调整航班排班、运力与票价,实现高季利润最大化,低季减少亏损

对机场/空管

依据季节性预判旅客流量,优化值机柜台、跑道使用效率

对乘客

利用低季低价窗口规划出行,可降低20%-30%出行成本

对市场竞争

季节性差异推动航司创新,如LCC在低季推出"早鸟票"

3. 疫情的三重冲击

1 需求端冲击

远程办公使商务出行减少30%以上,乘客偏好国内目的地

2 供给端冲击

全球供应链中断导致飞机交付延迟,航司运力恢复缓慢

3 定价逻辑冲击

疫情初期航司为维持现金流被迫大幅降价,可能重塑长期季节性定价习惯

二、文献综述

文献综述:核心研究主题

研究主题 代表学者 核心发现
机场类型与季节性 Halpern (2011) 旅游机场季节性强于大城市机场,国际/包机加剧季节性
机场盈利与季节性 Zuidberg (2017) 季节性与盈利呈倒"U"型关系
巴西航空定价 Póvoa & Oliveira (2008) 节假日使票价平均上涨8%-10%
巴西航空定价 Brito et al. (2021) 机场私有化后部分航线票价上涨5%
旅游季节性 Cazanova et al. (2014) 节日/假期是季节性核心驱动
航司季节性策略 Merkert & Webber (2018) 高季应优先提高上座率而非涨价

三、研究方法与数据

1. 数据来源与样本

数据来源:巴西民航局(ANAC) 2013年1月-2023年11月国内城市对月度数据

数据平台:哈佛Dataverse平台的AVDATA-BR-CP数据库

样本筛选:I. 导入数据


          * 从哈佛Dataverse导入航空票价数据集
          import delimited ///
          https://dataverse.harvard.edu/api/access/datafile/avdatabr_cp_cae/8171526, ///
          case(preserve) clear
          * case(preserve) 保留变量名原始大小写
          * clear 清除内存中现有数据

          * 筛选样本
        

样本筛选:II. 筛选核心样本


            * 从哈佛Dataverse导入航空票价数据集
            import delimited ///
            https://dataverse.harvard.edu/api/access/datafile/avdatabr_cp_cae/8171526, ///
            case(preserve) clear
            * case(preserve) 保留变量名原始大小写
            * clear 清除内存中现有数据

            * 保留2013年及以后的数据
            keep if Year>=2013

            * 生成平均飞机规模变量:可用座位数/出发班次
            gen mean_aircsize = nr_available_seats/nr_departures

            * 查看平均飞机规模的描述性统计
            summ mean_aircsize

            * 删除平均飞机规模小于50的观测值(排除小型航线)
            drop if mean_aircsize<50

            * 按城市对分组,生成每个城市对的观测数量
            bysort CityPair: gen nr_panels = [_N]

            * 查看面板观测数量的分布
            summ nr_panels

            * 删除观测数量少于60的城市对
            drop if nr_panels<60

            * 显示最终样本的面板结构信息
            * 城市对数量
            tab CityPair
            di "城市对数量 = " `r(r)'

            * 时间期数(月份)
            tab YearMonth
            di "时间期数 = " `r(r)'

            * 年份数量
            tab Year
            di "年份数量 = " `r(r)'

            * end of section
        

样本筛选:III. 最终样本统计

最终样本:487个城市对、131个月度时期(2013.01-2023.11)

共55,950个观测值

2. 核心变量:面板数据声明


          * 生成城市对编码变量
          egen k = group(CityPair)

          * 生成时间期编码变量
          egen t = group(YearMonth)

          * 声明面板数据格式:k为个体维度,t为时间维度
          tsset k t

          * 变量提取...
        

核心变量:变量提取


          * 生成城市对编码变量
          egen k = group(CityPair)

          * 生成时间期编码变量
          egen t = group(YearMonth)

          * 声明面板数据格式:k为个体维度,t为时间维度
          tsset k t
          
          * 从原始数据集中提取需要的变量
          keep k t price km_great_circle_distance jetfuel_price_org ///
          nr_revenue_pax market_concentration_hhi pc_load_factor ///
          YearMonth Year Month CityPair

          * 描述性统计:排除时间标识变量
          fsum, not(YearMonth k t) format(%10.2f)

          * 变量转换...
        

核心变量:变量转换


            * 生成城市对编码变量
            egen k = group(CityPair)

            * 生成时间期编码变量
            egen t = group(YearMonth)

            * 声明面板数据格式:k为个体维度,t为时间维度
            tsset k t
            
            * 从原始数据集中提取需要的变量
            keep k t price km_great_circle_distance jetfuel_price_org ///
            nr_revenue_pax market_concentration_hhi pc_load_factor ///
            YearMonth Year Month CityPair

            * 描述性统计:排除时间标识变量
            fsum, not(YearMonth k t) format(%10.2f)

            * 生成城市对编码变量
            egen k = group(CityPair)

            * 生成时间期编码变量
            egen t = group(YearMonth)

            * 声明面板数据格式:k为个体维度,t为时间维度
            tsset k t
            
            * 从原始数据集中提取需要的变量
            keep k t price km_great_circle_distance jetfuel_price_org ///
            nr_revenue_pax market_concentration_hhi pc_load_factor ///
            YearMonth Year Month CityPair

            * 描述性统计:排除时间标识变量
            fsum, not(YearMonth k t) format(%10.2f)

            * 生成模型所需的转换变量
            * 被解释变量:机票价格的对数
            gen AirFare = ln(price)

            * 核心解释变量:距离的对数
            gen Distance = ln(km_great_circle_distance)

            * 核心解释变量:燃油价格的对数
            gen FuelPrice = ln(jetfuel_price_org)

            * 核心解释变量:乘客密度的对数
            gen PaxDens = ln(nr_revenue_pax)

            * 核心解释变量:市场集中度的对数(乘以10000避免取对数问题)
            gen MktConc = ln(market_concentration_hhi*10000)

            * 核心解释变量:载客率的对数
            gen LoadFactor = ln(pc_load_factor)

            * 疫情虚拟变量:2020年2月-2022年4月期间为1,其余为0
            gen Pandemic = (YearMonth>=202002 & YearMonth<=202204)

            * 时间趋势变量:将时间标准化到0-2.18范围
            gen Trend = t/60

            * 季节性变量生成...
        

核心变量:季节性变量生成


            * 生成城市对编码变量
            egen k = group(CityPair)

            * 生成时间期编码变量
            egen t = group(YearMonth)

            * 声明面板数据格式:k为个体维度,t为时间维度
            tsset k t
            
            * 从原始数据集中提取需要的变量
            keep k t price km_great_circle_distance jetfuel_price_org ///
            nr_revenue_pax market_concentration_hhi pc_load_factor ///
            YearMonth Year Month CityPair

            * 描述性统计:排除时间标识变量
            fsum, not(YearMonth k t) format(%10.2f)

            * 生成城市对编码变量
            egen k = group(CityPair)

            * 生成时间期编码变量
            egen t = group(YearMonth)

            * 声明面板数据格式:k为个体维度,t为时间维度
            tsset k t
            
            * 从原始数据集中提取需要的变量
            keep k t price km_great_circle_distance jetfuel_price_org ///
            nr_revenue_pax market_concentration_hhi pc_load_factor ///
            YearMonth Year Month CityPair

            * 描述性统计:排除时间标识变量
            fsum, not(YearMonth k t) format(%10.2f)

            * 生成模型所需的转换变量
            * 被解释变量:机票价格的对数
            gen AirFare = ln(price)

            * 核心解释变量:距离的对数
            gen Distance = ln(km_great_circle_distance)

            * 核心解释变量:燃油价格的对数
            gen FuelPrice = ln(jetfuel_price_org)

            * 核心解释变量:乘客密度的对数
            gen PaxDens = ln(nr_revenue_pax)

            * 核心解释变量:市场集中度的对数(乘以10000避免取对数问题)
            gen MktConc = ln(market_concentration_hhi*10000)

            * 核心解释变量:载客率的对数
            gen LoadFactor = ln(pc_load_factor)

            * 疫情虚拟变量:2020年2月-2022年4月期间为1,其余为0
            gen Pandemic = (YearMonth>=202002 & YearMonth<=202204)

            * 时间趋势变量:将时间标准化到0-2.18范围
            gen Trend = t/60
            
            * 冬季假期虚拟变量:7月为1(巴西学校冬季假期)
            gen WintBreak = (Month==7)

            * 夏季搜索期虚拟变量:8-11月为1(夏季高峰前的搜索预订期)
            gen SummBrSearch = (Month==8 | Month==9 | Month==10 | Month==11)

            * 夏季高峰虚拟变量:12月-次年2月为1(巴西夏季旅游高峰)
            gen SummBreak = (Month==12 | Month==1 | Month==2)

            * 淡季虚拟变量:4-6月为1(旅游淡季)
            gen LowSeason = (Month==4 | Month==5 | Month==6)

            * end of section
        

核心变量:描述性统计


* 转换后变量的描述性统计
fsum AirFare Distance FuelPrice PaxDens MktConc ///
     LoadFactor Pandemic Trend
        

变量描述性统计表

变量 观测数 均值 标准差 最小值 最大值
机票价格(对数)55,9506.250.404.118.29
距离(对数)55,9506.650.684.687.95
燃油价格(对数)55,9501.100.280.431.82
乘客密度(对数)55,9508.631.472.2012.98
市场集中度(对数)55,9508.610.397.809.21
载客率(对数)55,9504.330.152.574.61
疫情虚拟变量55,9500.180.390.001.00
时间趋势55,9501.100.620.022.18

3. 模型设定:基础模型公式

无季节性控制:

\[ \begin{aligned} ln(AirFare_{it}) &= \beta_0 + \beta_1 ln(FuelPrice_{it}) + \beta_2 ln(PaxDens_{it}) \\ &+ \beta_3 ln(MktConc_{it}) + \beta_4 LoadFactor_{it} \\ &+ \beta_5 Pandemic_{it} + \beta_6 Trend_{it} + \alpha_i + \varepsilon_{it} \end{aligned} \]

有季节性控制:在上述公式中加入季节性变量

\[ \begin{aligned} &\beta_7 WintBreak_{it} + \beta_8 SummBrSearch_{it} \\ &+ \beta_9 SummBreak_{it} + \beta_{10} LowSeason_{it} \end{aligned} \]

基础模型:Stata代码(无季节性)


          * 无季节性控制的固定效应回归
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Pandemic Trend ///
            , absorb(CityPair)
          * absorb(CityPair):控制城市对固定效应

          * 存储回归结果
          est store WithoutSeas

          * 季节性控制...
        

基础模型:Stata代码(有季节性)


          * 无季节性控制的固定效应回归
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Pandemic Trend ///
            , absorb(CityPair)
          * absorb(CityPair):控制城市对固定效应

          * 存储回归结果
          est store WithoutSeas

          * 包含季节性控制的固定效应回归
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Pandemic Trend ///
            WintBreak SummBrSearch SummBreak LowSeason ///
            , absorb(CityPair)

          * 存储回归结果
          est store WithSeas

          * 结果输出...
        

基础模型:结果输出


          * 无季节性控制的固定效应回归
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Pandemic Trend ///
            , absorb(CityPair)
          * absorb(CityPair):控制城市对固定效应

          * 存储回归结果
          est store WithoutSeas

          * 包含季节性控制的固定效应回归
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Pandemic Trend ///
            WintBreak SummBrSearch SummBreak LowSeason ///
            , absorb(CityPair)

          * 存储回归结果
          est store WithSeas

          * 使用esttab输出两个模型的对比表,并保存为表2
          esttab 	WithoutSeas WithSeas ///
              , nocons nose not nogaps noobs ///
              b(%9.4f) varwidth(14) brackets ///
              aic(%9.0fc) bic(%9.0fc) ar2 scalar(N) sfmt(%9.0fc) ///
              addnote("Notes: Fixed Effect estimation")
          * nocons: 不显示常数项
          * nose: 不显示标准误
          * not: 不显示t统计量
          * brackets: 标准误放在括号中

          * end of section
        

表2 有无季节性控制的回归结果对比

变量 (1) 无季节性 (2) 有季节性
燃油价格0.2931***0.2897***
乘客密度-0.0888***-0.0937***
市场集中度0.0854***0.0859***
载客率0.3121***0.2935***
疫情变量-0.1674***-0.1690***
时间趋势0.0215***0.0180***
冬季假期--0.0009
夏季搜索期-0.0593***
夏季高峰-0.0145***
淡季--0.0452***
调整R²0.5790.588
AIC8,5397,265
BIC8,6017,363
观测数55,95055,950

* p<0.05, ** p<0.01, *** p<0.001

精细化季节性模型:生成月度变量


* 生成更细粒度的月度虚拟变量
* 夏季前4个月:8月
gen SummBreak_bef4 = (Month==8)
* 夏季前3个月:9月
gen SummBreak_bef3 = (Month==9)
* 夏季前2个月:10月
gen SummBreak_bef2 = (Month==10)
* 夏季前1个月:11月
gen SummBreak_bef1 = (Month==11)
* 夏季开始月:12月
gen SummBreak_aft0 = (Month==12)
* 夏季后1个月:1月
gen SummBreak_aft1 = (Month==1)
* 夏季后2个月:2月
gen SummBreak_aft2 = (Month==2)
* 淡季第1个月:4月(基准组为3月)
gen LowSeason_aft1 = (Month==4)
* 淡季第2个月:5月
gen LowSeason_aft2 = (Month==5)
* 淡季第3个月:6月
gen LowSeason_aft3 = (Month==6)
        

精细化季节性模型:回归与输出


* 包含精细化季节性的固定效应回归
* 使用通配符 * 包含所有相关变量
reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
	Pandemic Trend ///
	WintBreak SummBreak_* LowSeason_* ///
	, absorb(CityPair)

* 存储回归结果
est store GranularSeas

* 使用esttab输出细粒度季节性模型的结果表,并保存为表3
esttab 	GranularSeas ///
		, nocons nose not nogaps noobs ///
		b(%9.4f) varwidth(14) brackets ///
		aic(%9.0fc) bic(%9.0fc) ar2 scalar(N) sfmt(%9.0fc) ///
		addnote("Notes: Fixed Effect estimation")
        

表3 精细化季节性模型回归结果

变量系数
燃油价格0.2910***
乘客密度-0.0941***
市场集中度0.0865***
载客率0.2950***
疫情变量-0.1677***
时间趋势0.0167***
冬季假期(7月)-0.0008
夏季前4月(8月)0.0563***
夏季前3月(9月)0.0688***
夏季前2月(10月)0.0812***
夏季前1月(11月)0.0315***
夏季开始(12月)0.0609***
夏季后1月(1月)-0.0156**
夏季后2月(2月)0.0020
淡季1月(4月)-0.0492***
淡季2月(5月)-0.0380***
淡季3月(6月)-0.0482***
调整R²0.591
观测数55,950

精细化季节性模型:系数图


* 使用coefplot绘制GranularSeas模型中季节性变量的系数图,并显示95%置信区间,输出结果为图1
coefplot GranularSeas ///
		, keep(WintBreak SummBreak_* SummBreak_* LowSeason_*) ///
		xline(0, lcolor(green) lpattern(dash)) scheme(s2color) ///
		level(95) recast(connected) lpattern(longdash) lwidth(0.1)
* keep(): 指定要绘制的变量
* xline(0): 在x=0处添加参考线
* scheme(s2color): 使用s2color配色方案
* level(95): 95%置信区间
* recast(connected): 连线图形式
        

图1 价格季节性系数图

结论:价格季节性呈"先升后降、峰谷分明"特征,10月为全年价格高点,4月为低点

疫情前后事件研究:疫情前样本


          * 疫情前样本回归(2013年1月-2020年1月)
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Trend WintBreak SummBreak_* LowSeason_* ///
            if YearMonth<=202001, absorb(CityPair)

          * 存储疫情前回归结果
          est store PrePandemic

          *  疫情后样本回归...
        

疫情前后事件研究:疫情后样本


          * 疫情前样本回归(2013年1月-2020年1月)
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Trend WintBreak SummBreak_* LowSeason_* ///
            if YearMonth<=202001, absorb(CityPair)

          * 存储疫情前回归结果
          est store PrePandemic
          
          * 疫情后样本回归(2022年5月-2023年11月)
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Trend WintBreak SummBreak_* LowSeason_* ///
            if YearMonth>202204, absorb(CityPair)

          * 存储疫情后回归结果
          est store PostPandemic

          * 结果输出...
        

疫情前后事件研究:结果输出


          * 疫情前样本回归(2013年1月-2020年1月)
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Trend WintBreak SummBreak_* LowSeason_* ///
            if YearMonth<=202001, absorb(CityPair)

          * 存储疫情前回归结果
          est store PrePandemic
          
          * 疫情后样本回归(2022年5月-2023年11月)
          reghdfe AirFare FuelPrice PaxDens MktConc LoadFactor ///
            Trend WintBreak SummBreak_* LowSeason_* ///
            if YearMonth>202204, absorb(CityPair)

          * 存储疫情后回归结果
          est store PostPandemic

          * 使用esttab将疫情前与疫情后两组结果并列输出,输出结果为表4
          esttab 	PrePandemic PostPandemic ///
              , nocons nose not nogaps noobs mtitles ///
              b(%9.4f) varwidth(14) brackets ///
              aic(%9.0fc) bic(%9.0fc) ar2 scalar(N) sfmt(%9.0fc) ///
              addnote("Notes: Dependent variable - AirFare" ///
              "Fixed Effect estimation")
          * mtitles: 显示模型标题

          * end
        

表4 疫情前后回归结果对比(上半部分)

变量疫情前疫情后
燃油价格0.1677***0.0681*
乘客密度-0.1608***-0.1466***
市场集中度0.0889***-0.0582***
载客率0.3020***0.1889***
时间趋势-0.0244***0.2301***

表4 疫情前后回归结果对比(下半部分)

变量疫情前疫情后
冬季假期(7月)0.0668***-0.0293*
夏季前4月(8月)0.0822***0.0688***
夏季前3月(9月)0.0878***0.0876***
夏季前2月(10月)0.1178***0.0559***
夏季前1月(11月)0.0686***0.0313*
夏季开始(12月)0.1107***0.0546***
夏季后1月(1月)0.0263***-0.0176
夏季后2月(2月)0.0181**-0.0486**
淡季1月(4月)-0.0265***-0.1048***
淡季2月(5月)-0.0125*-0.0269*
淡季3月(6月)-0.0138*-0.0444**
调整R²0.6470.722
观测数37,4418,373

疫情前后事件研究:系数对比图


* 用coefplot将疫情前后模型的季节性系数作对比图,便于可视化结构性变化,输出结果为图2
coefplot PrePandemic PostPandemic ///
		, keep(WintBreak SummBreak_* SummBreak_* LowSeason_*) ///
		xline(0, lcolor(green) lpattern(dash)) scheme(s2color) ///
		level(95) recast(connected) lpattern(longdash) lwidth(0.1)
* 同时显示疫情前和疫情后的系数,便于对比结构性变化
        

图2 疫情前后季节性系数对比图

结论:疫情后季节性"峰谷差扩大",呈现"去集中化"特征

四、实证结果核心结论

1. 基础模型结果解读

  • 核心驱动因素:
    • 油价每上涨10%,票价上涨2.9%
    • 乘客密度每增加10%,票价下降0.935%(规模经济)
    • 市场集中度每提高10%,票价上涨0.86%(垄断溢价)
  • 疫情影响:疫情期间票价平均下降16.7%
  • 季节性特征:
    • 夏季前搜索期票价最高,较基准3月上涨5.9%
    • 淡季票价最低,较3月下降4.95%

2. 精细化季节性模型关键发现

  • 价格上涨周期:8月开始涨价,10月达峰值(8.12%),11月涨幅骤降至3.15%(节假日增加运力)
  • 夏季后期波动:1月票价较3月下降1.56%(节后促销策略)
  • 淡季稳定性:4-6月票价持续低于基准,4月降幅最大(-4.92%)

3. 疫情前后事件研究核心发现

  • 成本敏感度变化:疫情后燃油价对票价影响下降(0.1677→0.0681)
  • 市场结构反转:疫情前集中度推高票价(0.0889***),疫情后转为抑制票价(-0.0582***)
  • 长期趋势逆转:疫情前每5年票价下降2.5%,疫情后每5年上涨23%
  • 季节性重构:
    • 7月假期:疫情前上涨6.68%,疫情后下降2.93%(符号反转)
    • 10月峰值:涨幅从11.78%降至5.59%
    • 4月淡季:降幅从2.65%扩大至10.48%

五、研究结论与建议

1. 核心结论

  • 季节性重构:
    • 7月假期票价从涨转降(-2.93%)
    • 10月峰值涨幅减半(5.59% vs 11.78%)
    • 4月淡季降幅扩大3倍(-10.48% vs -2.65%)
  • 长期趋势逆转:疫情后每5年票价上涨23%,主因全球飞机短缺与需求复苏的供需错配
  • 驱动因素切换:疫情前票价由"成本+垄断"驱动,疫情后转为"竞争+运力"驱动

2. 实践建议:对航司

  • 夏季前(8-9月)适度控价,避免10月峰值过高导致需求流失
  • 7月推出"亲子错峰票",抵消需求降温
  • 淡季(4月)推出"套票",提升运力利用率
  • 优先租赁飞机(而非等待新飞机交付),缓解运力短缺

实践建议:对乘客

  • 休闲出行:优先选择4月、6月购票,较旺季节省10%-15%成本
  • 商务出行:11月(节假日多)票价较低,可灵活安排行程

3. 未来研究

  • 处理组 vs 控制组研究(经济增长差异)
  • 多固定效应扩展(区域固定效应)
  • 旅游航线专项分析
  • 长期趋势反转机制(运力-票价回归模型)

感谢观看!