第1章:Pandas数据处理——REITs量化分析的基石

说实话,我刚开始做REITs量化分析的时候,最头疼的不是策略设计,而是数据清洗。你想想看,从不同数据源拿到的REITs数据,格式五花八门,缺失值、异常值满天飞。那时候我就在想,要是有一套标准化的处理流程该多好。

后来我发现,Pandas就是解决这个问题的利器。它就像一把瑞士军刀,能帮你把乱七八糟的数据整理得井井有条。今天我就带你从零开始,掌握Pandas在REITs分析中的核心用法。

本章核心内容:

  • Pandas两大核心数据结构:Series和DataFrame
  • 数据清洗与预处理实战技巧
  • REITs专属财务指标计算:FFO、AFFO、NAV
Pandas REITs量化分析 核心数据结构 Series DataFrame 数据清洗预处理 缺失值处理 异常值检测 REITs财务指标 FFO AFFO NAV 三大模块环环相扣,构建REITs量化分析基础

1.1 Pandas核心数据结构:Series与DataFrame

Pandas里最常用的两个数据结构,说白了就是Series和DataFrame。Series像一列数据,DataFrame像一张表格。我刚开始学的时候,总把这两个搞混,后来发现一个简单的记忆方法:DataFrame是多个Series拼起来的。

1.1.1 Series:一维数据容器

Series可以理解为一个带标签的数组。每个元素都有索引,你可以通过索引快速定位数据。举个例子,我们拿REITs的每日收盘价来说:

import pandas as pd

# 创建REITs收盘价Series
reit_prices = pd.Series([25.3, 25.8, 26.1, 25.9, 26.4],
                        index=['2024-01-02', '2024-01-03', 
                               '2024-01-04', '2024-01-05', '2024-01-06'],
                        name='REIT_Close')
print(reit_prices)

输出结果会显示索引和对应的值。我个人习惯用日期做索引,这样后续做时间序列分析会方便很多。

小技巧:创建Series时指定name参数,后续合并到DataFrame时列名会自动匹配,省去重命名的麻烦。

1.1.2 DataFrame:二维表格数据结构

DataFrame才是我们真正干活的主力。它就像Excel表格,有行有列。我在项目中经常用它来存储多只REITs的历史数据:

# 创建REITs多只股票数据
reit_data = pd.DataFrame({
    'REIT_A': [25.3, 25.8, 26.1, 25.9, 26.4],
    'REIT_B': [32.1, 31.8, 32.5, 32.2, 31.9],
    'REIT_C': [18.5, 18.9, 18.7, 19.1, 19.0]
}, index=['2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-06'])

print(reit_data.head())  # 查看前5行

你想想看,如果手动处理这些数据,得花多少时间?用Pandas几行代码就搞定了。

1.2 数据清洗与预处理实战

真实世界的数据从来不会这么干净。我记得有一次从某数据源抓取REITs财务数据,结果发现20%的数据都是缺失的。嗯,这时候数据清洗就派上用场了。

1.2.1 缺失值处理

缺失值在Pandas里显示为NaN。常见的处理方法有三种:删除、填充、插值。我个人建议,如果缺失比例低于5%,直接删除;如果高于5%,用均值或前向填充。

# 模拟含缺失值的REITs数据
import numpy as np

reit_with_na = reit_data.copy()
reit_with_na.loc['2024-01-03', 'REIT_A'] = np.nan
reit_with_na.loc['2024-01-05', 'REIT_B'] = np.nan

# 方法1:删除含缺失值的行
clean_data = reit_with_na.dropna()
print("删除后数据:\n", clean_data)

# 方法2:用前一个值填充
filled_data = reit_with_na.fillna(method='ffill')
print("前向填充后:\n", filled_data)

注意:千万不要在金融时间序列数据上使用均值填充!我曾经犯过这个错误,结果导致回测结果严重失真。REITs价格有趋势性,用均值填充会破坏这种趋势。

1.2.2 异常值检测

异常值在REITs数据中很常见,比如突然的股价异动。我一般用Z-score方法检测:

from scipy import stats

# 计算Z-score
z_scores = np.abs(stats.zscore(reit_data['REIT_A']))
outliers = reit_data[z_scores > 3]
print(f"检测到{len(outliers)}个异常值")

为什么用3倍标准差?这是统计学上的经验值。当然,你也可以根据实际情况调整阈值。

1.3 REITs财务指标计算

终于到了最核心的部分。FFO、AFFO、NAV这三个指标,是REITs分析的灵魂。不懂这三个指标,你都不好意思说自己懂REITs。

1.3.1 FFO(营运现金流)

FFO全称是Funds From Operations,说白了就是REITs真正赚到的钱。为什么不用净利润?因为REITs的折旧摊销太大了,净利润根本反映不了真实经营情况。

# 计算FFO
def calculate_ffo(net_income, depreciation, amortization, 
                  gains_on_sale):
    """
    计算REITs的FFO
    FFO = 净利润 + 折旧与摊销 - 资产出售收益
    """
    ffo = net_income + depreciation + amortization - gains_on_sale
    return ffo

# 示例数据
reit_financials = {
    'net_income': 1000000,      # 净利润
    'depreciation': 200000,     # 折旧
    'amortization': 50000,      # 摊销
    'gains_on_sale': 100000     # 资产出售收益
}

ffo = calculate_ffo(**reit_financials)
print(f"FFO = ${ffo:,.2f}")

关键点:FFO是REITs分红的基石。我见过很多新手直接用净利润来估值,结果低估了REITs的真实价值。记住,REITs看FFO,不看净利润。

1.3.2 AFFO(调整后营运现金流)

AFFO是FFO的升级版。它进一步剔除了维持性资本支出。为什么叫「调整后」?因为FFO里还包含了一些非现金项目,AFFO把这些都剔除了,更接近真实的可分配现金流。

def calculate_affo(ffo, maintenance_capex, straight_line_rent,
                   stock_compensation):
    """
    计算AFFO
    AFFO = FFO - 维持性资本支出 - 直线法租金调整 - 股权激励
    """
    affo = ffo - maintenance_capex - straight_line_rent - stock_compensation
    return affo

# 接上面的FFO数据
maintenance_capex = 80000      # 维持性资本支出
straight_line_rent = 20000     # 直线法租金调整
stock_compensation = 15000     # 股权激励

affo = calculate_affo(ffo, maintenance_capex, 
                      straight_line_rent, stock_compensation)
print(f"AFFO = ${affo:,.2f}")

我个人习惯用AFFO来估算REITs的合理分红水平。如果AFFO持续高于分红,说明分红有保障;反之就要警惕了。

1.3.3 NAV(净资产价值)

NAV就是REITs手里资产的市场价值减去负债。说白了,如果今天把REITs清盘了,每股能分到多少钱。

def calculate_nav(property_value, cash, other_assets, 
                  total_liabilities, shares_outstanding):
    """
    计算每股NAV
    NAV = (资产市场价值 + 现金 + 其他资产 - 总负债) / 总股本
    """
    nav_per_share = (property_value + cash + other_assets 
                     - total_liabilities) / shares_outstanding
    return nav_per_share

# 示例数据
nav_inputs = {
    'property_value': 50000000,    # 物业市场价值
    'cash': 2000000,               # 现金
    'other_assets': 1000000,       # 其他资产
    'total_liabilities': 30000000, # 总负债
    'shares_outstanding': 5000000  # 总股本
}

nav = calculate_nav(**nav_inputs)
print(f"每股NAV = ${nav:.2f}")

实战经验:当REITs的市场价格低于NAV时,可能存在投资机会。但要注意,NAV的估算依赖于物业估值,而估值本身就有主观性。我一般会参考多家机构的估值取中位数。

1.4 综合实战:构建REITs财务分析流水线

好了,现在我们把所有知识串起来。下面是一个完整的REITs财务分析流水线:

class REITAnalyzer:
    """REITs财务分析器"""
    
    def __init__(self, financial_data):
        self.data = financial_data
        self.ffo = None
        self.affo = None
        self.nav = None
    
    def clean_data(self):
        """数据清洗"""
        self.data = self.data.dropna()
        # 去除异常值
        z_scores = np.abs(stats.zscore(self.data.select_dtypes(
            include=[np.number])))
        self.data = self.data[(z_scores < 3).all(axis=1)]
        return self
    
    def calculate_all_metrics(self):
        """计算所有财务指标"""
        self.ffo = calculate_ffo(
            self.data['net_income'],
            self.data['depreciation'],
            self.data['amortization'],
            self.data['gains_on_sale']
        )
        
        self.affo = calculate_affo(
            self.ffo,
            self.data['maintenance_capex'],
            self.data['straight_line_rent'],
            self.data['stock_compensation']
        )
        
        self.nav = calculate_nav(
            self.data['property_value'],
            self.data['cash'],
            self.data['other_assets'],
            self.data['total_liabilities'],
            self.data['shares_outstanding']
        )
        
        return {
            'FFO': self.ffo,
            'AFFO': self.affo,
            'NAV': self.nav
        }

# 使用示例
sample_data = pd.DataFrame({
    'net_income': [1000000],
    'depreciation': [200000],
    'amortization': [50000],
    'gains_on_sale': [100000],
    'maintenance_capex': [80000],
    'straight_line_rent': [20000],
    'stock_compensation': [15000],
    'property_value': [50000000],
    'cash': [2000000],
    'other_assets': [1000000],
    'total_liabilities': [30000000],
    'shares_outstanding': [5000000]
})

analyzer = REITAnalyzer(sample_data)
results = analyzer.clean_data().calculate_all_metrics()
print("分析结果:")
for key, value in results.items():
    print(f"{key}: ${value:,.2f}")

这个类封装了我们今天学的所有内容。你想想看,以后分析新的REITs,只需要传入数据,调用两个方法就能得到核心指标,是不是很方便?

本章总结:

  • Series和DataFrame是Pandas的两大核心,DataFrame是分析主力
  • 数据清洗要谨慎,金融数据尤其注意不要破坏时间序列特性
  • FFO、AFFO、NAV是REITs分析的三大支柱,必须熟练掌握
  • 封装成类可以提高代码复用性,建议在实际项目中采用

说实话,这些内容我反复讲过很多次,但每次都有新的体会。REITs量化分析这条路,数据是基础,指标是工具,策略才是灵魂。今天我们把基础打牢了,后面才能走得更远。

专注资料整理