大数据经济学 ¶
约 32574 个字 8 张图片 预计阅读时间 127 分钟
注:直接 jupyter notebook 转 html 搬运过来的,可能有一些乱码;
课程学习建议:其实不难,善用B站、多查、多练、多运用,忌硬背;
一个好方法是想一个你喜欢的项目,比如分析某个平台数据,分析小说、游戏、电视剧等等,在这个过程中,一边问ai一边学。
Concepts¶
# 忽略 FutureWarning
import warnings
warnings.filterwarnings("ignore", category=FutureWarning)
with open("/tmp/foo.txt") as file:
data = file.read()
line = file.readline()
lines = file.readlines()
with open('example.txt', 'w/w+') as file:
file.write('Hello, World!\n')
with open('example.txt', 'a/a+') as file: #追加模式
file.write('Hello, World!\n')
try:
with open('example.txt', 'r') as file:
content = file.read()
print(content)
except FileNotFoundError:
print('文件不存在!')
except PermissionError:
print('无权限访问文件!')
except Exception as e:
print(f'发生未知错误:{e}')
转义字符 \ ¶
print("I\'m ok.") # output: I'm ok.
print(r'D:\Users') # output: D:\Users
print("I\'m learning\nPython.") # output:I'm learning 换行 python.
print('\\\n\\') #output:\ 换行 \
print('%%') #output:%
字符串编码 ¶
# str-unicode;bytes EN-ascii CN-utf-8
ord('A') # output: 65
chr(65) # output: 'A'
ord('中') # output: 20013
# 该函数可用于字母、数字排序校对
print('A'.encode('ascii'))# output: b'A'
print(b'A'.decode(ascii)) # output: 'A'
print("你好".encode('utf-8')) # output: b'\xe4\xbd\xa0\xe5\xa5\xbd'
len('你好') # output: 2
len(b'\xe4\xbd\xa0\xe5\xa5\xbd') # output: 6
# 1个中文字符经过UTF-8编码后通常会占用3个字节,
#而1个英文字符只占用1个字节。
字符串处理 ¶
## 添加前后缀
x=['600050','600976','002024','000063']
l=[i+'.XSHG' for i in x if i[0]=='6'] + [i+'.XSHE' for i in x if i[0]!='6']
## 补齐前位
x=[1,2,7,600001]
def trans_code(code):
new_code=str(code)
while len(new_code)<6:
new_code='0'+new_code
return new_code
l=[trans_code(i) for i in x]
## 自定义填充
original_string = "example"
width = 10
fill_char = '*'
left_aligned = original_string.ljust(width, fill_char) # 输出: example***
right_aligned = original_string.rjust(width, fill_char)# 输出: ***example
centered = original_string.center(width, fill_char)# 输出: *example**
number = 42
left_pad = "{:05d}".format(number) # 输出: 00042
right_pad = "{:<08.3f}".format(number)# 输出: 42.123000
## 格式化
name = "Alice"
age = 30
formatted_string1 = f"My name is {name} and I am {age} years old."
f_number1 = f"{number:.2f}"
formatted_string2 = "My name is {} and I am {} years old.".format(name, age)
f_number2 = "{:.2f}".format(number)
formatted_string3 = "My name is %s and I am %d years old." % (name, age)
f_number3 = "%.2f" % number
## 替换.replace
ori = "hello world, hello universe"
rep = ori.replace("hello", "hi", 1)# 输出: hi world, hello universe
## 分割.split
ori = "apple,banana,cherry"
split_list = ori.split(',')
## 连接.join
words = ["apple", "banana", "cherry"]
jo = ", ".join(words)# 输出: apple, banana, cherry
## 去重
df['a'].drop_duplicates()
正则表达式字符串 ¶
正则表达式引擎
正则表达式对象:正则表达式引擎编译表达式字符串得到的对象,包含应如何进行匹配的信息
匹配结果:正则表达式对象对文本进行匹配后得到的结果,包含了这次成功匹配的信息
re.findall(' 表达式 ', 文本 ) 匹配所有可匹配的 ( 返回列表 )
控制字符 | 描述 |
---|---|
^ | 反义 |
[] | 匹配一个字符 |
* | 重复零次或多次 |
+ | 重复 1 次或多次 |
\ | 转义 |
\s | 匹配空白(空格、制表符和换行) |
[0-9]+ | 匹配数字 |
[\s] | 匹配空格 |
[^\s] 或 [^<] | 匹配非空格的字符串 |
[^"] | 匹配非双引号的字符串 |
() | 捕获组 |
()? | 可选 |
import re
s='''董明珠女士'''
re.findall('[0-9]+', s)#匹配数字
s1=re.findall('[\u4e00-\u9fa5]',s) #剔除非汉字字符
s2=''.join(s1)
pattern='<td><div align="center">([0-9]+)<' #数字
x=re.findall(pattern, string)
relink='<a href="([^\"]+)">' #链接
cinfo=re.findall(relink,string)
pattern2='align=\"center\">([^<]+)</div' #同类信息
#提取所有连接
url_pattern = r'href=[\'"]?([^\'" >]+)'
#提取日期
date_pattern = r'\b(?:\d{4}[-/]\d{2}[-/]\d{2} | \d{2}[-/]\d{2}[-/]\d{4})\b'
#提取文本
html_tag_pattern = r'<[^>]+>(.*?)<\/[^>]+>'
#提取图片url
img_url_pattern = r'src=[\'"]?([^\'" >]+)'
#提取标签
header_pattern = r'<h[1-6]>(.*?)<\/h[1-6]>'
#三个捕获组对应后面的cols
re.findall("jishu\\([0-9]+,([0-9]+),'\n
([^']+)','[0-9]+'\\)\" title=\"([^\"]+)"
文本分析 ¶
文本记录了人类互动、交流和文化。
- 在金融领域,来自财经新闻、社交媒体和公司文件的文本被用来预测资产价格走势,并研究新信息的因果影响。
- 在宏观经济学中,文本用于预测通货膨胀和失业的变化,并估计政策不确定性的影响。
- 在媒体经济学中,来自新闻和社交媒体的文本被用来研究政治倾向的驱动因素和影响。
- 在产业组织和市场营销,文本从广告和产品评论来研究消费者决策的驱动因素。
- 在政治经济学中,政治家演讲的文本被用来研究政治议程和辩论的动态。
文本具有高维、非结构化、信息密度低的特点。
经济学中主流方法
- 基于词汇表的词频分析
- 这类方法使用预先定义的词汇表来分析文本。
- 通常基于词频等统计指标来计算文本的特征。
- 方法简单易用,但可能无法捕捉到文本的语义信息。
- 基于向量的回归方法
- 这类方法将文本转化为数值向量特征,并使用回归模型进行分析。
- 可以考虑到文本的结构和语义信息,适用于预测和建模任务。
- 常见的向量回归方法包括线性回归、逻辑回归等。
- 基于 AI 的文本倾向分析
- 这类方法使用人工预定义标签的语料库给机器学习
- 机器建立黑箱模型建立标签与语料库的关系
- 定义标签的语料库足够大时,模型能够捕捉与语义信息
中文文本分析的特点
- 中文语义的基本单元是词而不是字
- 中文词无自然间隔,需要分词
- 不同的分词方法可能具有不同的含义
def count_chinese_word(filepath, encoding='gbk'):
_dict = {}
with open (filepath, 'r', encoding= encoding ,errors="ignore") as txt_file:
for uchar in txt_file.read():
_dict[uchar]=_dict.get(uchar,0)+1
return _dict
def is_chinese(uchar):
if uchar >= u'\u4E00' and uchar <= u'\u9FA5':
return True
else:
return False
jieba¶
四种分词模式:
- 精确模式:试图将句子最精确地切开,适合文本分析 jieba.cut(string)
- 全模式:把句子中所有的可以成词的词语都扫描出来 , 速度非常快,但是不能解决歧义 jieba.cut(string,cut_all=True)
- 搜索引擎模式:在精确模式的基础上,对长词再次切分,适合用于搜索引擎分词 cut_for_search(string)
- 深度学习模式:利用 PaddlePaddle 深度学习框架,训练序列标注(双向 GRU)网络模型实现分词,支持词性标注。
#精确模式
import jieba
seg1 = jieba.cut("迅雷不及掩耳盗铃儿响叮当仁不让我们荡起双桨之势") # 默认精确模式
",".join(seg1)
#output'迅雷不及,掩耳盗铃,儿响,叮,当仁不让,我们,荡起,双桨,之势'
#全模式
seg2 = jieba.cut("迅雷不及掩耳盗铃儿响叮当仁不让我们荡起双桨之势",cut_all=True)
",".join(seg2)
#output'迅雷,迅雷不及,迅雷不及掩耳,不及,掩耳,掩耳盗铃,儿,响叮当,叮当,当仁不让,不让,我们,荡起,双桨,之,势'
#搜索引擎模式
seg3 = jieba.cut_for_search("迅雷不及掩耳盗铃儿响叮当仁不让我们荡起双桨之势") #搜索引擎模式
",".join(seg3)
#output'迅雷,不及,迅雷不及,掩耳,掩耳盗铃,儿响,叮,不让,当仁不让,我们,荡起,双桨,之势'
#paddel深度学习模式
#精确模式和paddle模式效果差异并不大,仅在一些名称词表现出色一些,但paddle模式非常耗时,多50倍以上
seg4 = jieba.cut("迅雷不及掩耳盗铃儿响叮当仁不让我们荡起双桨之势",use_paddle=True)
",".join(seg4)
#output'迅雷不及,掩耳盗铃,儿响,叮,当仁不让,我们,荡起,双桨,之势'
#分标签
import jieba.posseg as pseg
seg5 = pseg.cut("迅雷不及掩耳盗铃儿响叮当仁不让我们荡起双桨之势",use_paddle=True) #paddle模式分析出词性
list(seg5)
'''output:[pair('迅雷不及', 'i'),pair('掩耳盗铃', 'i'),pair('儿响', 'n'),pair('叮', 'v'),pair('当仁不让', 'i'),
pair('我们', 'r'),pair('荡起', 'v'),pair('双桨', 'n'),pair('之', 'u'),pair('势', 'ng')]'''
时间处理 ¶
- 1.time python 包
- 2.datetime python 包
- 3.pandas.to_datetime
time.localtime(secs)
- 将时间戳转换为当前的 struct time 结构
- secs 如果没有提供,默认使用前时间 time.time()
- 得到当前时间戳 time.mktime(t)
- 将 struct time 转换为时间戳 time.strptime(string[, format])
- 将字符串时间转换为 struct time
time.strftime(format[, t])
- 将 struct time 转换为字符串
- t 默认为:"%a %b %d %H:%M:%S %Y"
import time
time.time()
time.localtime(secs) # 参数为时间戳秒数,为空则取当前时间
# output: time.struct_time(tm_year,tm_mon,tm_mday,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst)
time.mktime(t) # 结构化时间转时间戳
# 定制函数字符串转时间戳
def s2timestamp(day):
st=time.strptime(day, "%Y-%m-%d")
return time.mktime(st)
time.strptime(string,'%Y-%m-%d %H:%M:%S') # 字符串转结构化
time.strftime('%Y-%m-%d %H:%M:%S', t) # 结构化转字符串
# 定制函数时间戳转字符串
def t2str(t):
st=time.localtime(t)
return time.strftime("%Y-%m-%d",st)
# 时间加法
s = 某date
t2s(s2t(s)+需加的天数*24*3600)
def add_day(day,num):
smp1=time.mktime(time.strptime(day,"%Y-%m-%d"))
smp2=smp1+24*3600*num
day2s=time.localtime(smp2)
return time.strftime("%Y-%m-%d",day2s)
# 时间减法
t1=s2t('2025-10-01')
t2=s2t('2022-01-01')
(t1-t2)/3600/24
def get_daysdiff(day1,day2):
smp1=time.mktime(time.strptime(day1,"%Y-%m-%d"))
smp2=time.mktime(time.strptime(day2,"%Y-%m-%d"))
return int((smp2-smp1)/3600/24)
# 提取年份
def to_yearmonth(x):
t=time.strptime(x,'%b-%y') #原本不合规的时间字符串长什么样
y=time.strftime("%Y",t)
m=time.strftime("%m",t)
s = y+m
return s # 只要年份和月份就单独输出
df['Y']=df['month'].apply(to_y)
# 提取week
def get_week(day): # time struct 中的 tm_day中的0表示星期一
return time.strptime(day,"%Y-%m-%d").tm_wday+1
%z Time zone offset from UTC.
%a Sun
%A Sunday
%b Oct
%B October
%c Locale's appropriate date and time representation.
%I 12小时制搭配%p
%p Locale's equivalent of either AM or PM.
%W 周数,星期一为始 %Y_%W
%j 一年中的第几天
# datetime
def add_day(day, num):
"""
添加指定天数到给定日期
:param day: str, 日期字符串,格式为 "%Y-%m-%d"
:param num: int, 需要添加的天数
:return: str, 新的日期字符串
"""
date_obj = datetime.strptime(day, "%Y-%m-%d")
new_date = date_obj + timedelta(days=num)
return new_date.strftime("%Y-%m-%d")
def get_week(day):
"""
获取给定日期的星期几
:param day: str, 日期字符串,格式为 "%Y-%m-%d"
:return: int, 星期几(1-7)
"""
date_obj = datetime.strptime(day, "%Y-%m-%d")
return date_obj.weekday() + 1
def get_week_start_end(day):
"""
获取给定日期所在周的开始日和结束日
:param day: str, 日期字符串,格式为 "%Y-%m-%d"
:return: tuple, (开始日, 结束日)
"""
date_obj = datetime.strptime(day, "%Y-%m-%d")
start_of_week = date_obj - timedelta(days=date_obj.weekday())
end_of_week = start_of_week + timedelta(days=6)
return start_of_week.strftime("%Y-%m-%d"), end_of_week.strftime("%Y-%m-%d")
# pandas
df['date'] = pd.to_datetime(df['date'],format='') #字符串转换为日期格式
df['days_interval'] = df['date'].diff().dt.days # 计算相邻日期的天数差
缺失值处理 ¶
- 向前填充法 :df.ffill()
- 向后填充法 :df.bfill()
- 线性插值法 : 线性插入新值
- 二次插值法 : 而此项插入新值
- 最近邻均值法 : 对左右邻居求平均
- 季节均值法 : 取之前同季的均值
# 缺失值查询
df.isnull()
df['col1'].isnull()
df.isnull.sum() #axis=0/1,0列1行
# 筛选有缺失值的行
df.loc[df.isnull().any(1)]
df.loc[~(df.isnull().any(1))] #取反
# 筛选有缺失值的列
df.loc[:,df.isnull().any()]
# 补
df.fillna(0)
df.fillna(method = 'ffill')#行数据向下填充
df.fillna(method = 'ffill',axis=1)#列数据向右填充
DataFrame.interpolate(method='linear'/'pad'/'time'/'nearest'/'cubic'/, axis=0, limit=None, inplace=False, limit_direction=None, limit_area=None, downcast=None, **kwargs)
随机数 ¶
import random
## 基本随机数生成
random.random() #生成一个 [0.0, 1.0) 范围内的随机浮点数。
random.uniform(a, b)#生成一个 [a, b] 或 (a, b) 范围内的随机浮点数,包括边界。
random.randint(a, b)#生成一个 [a, b] 范围内的随机整数,包括边界。
random.randrange(start, stop[, step])#生成一个从 start 开始,到 stop 结束(不包括 stop),步长为 step 的随机整数。
random.choice(seq)# 从非空序列 seq 中随机选择一个元素。
random.choices(population, weights=None/cum_weights=None, k=1) #从 population 序列中随机选择 k 个元素,指定权重。
random.sample(population, k)#从 population 序列中随机选择 k 个不重复的元素。
## 序列操作
random.shuffle(x[, random])#就地打乱可变序列 x 的顺序。
random.permutation(x)#返回一个新的序列,包含 x 中所有元素的随机排列。
## 分布相关的随机数
random.paretovariate(alpha)#alpha=1.5 二八分布帕累托
random.gauss(mu, sigma)#生成一个符合均值为 mu,标准差为 sigma 的高斯分布(正态分布)的随机浮点数。
random.expovariate(lambd)#生成一个符合指数分布的随机浮点数,lambd 是速率参数。
random.lognormvariate(mu, sigma)#生成一个符合对数正态分布的随机浮点数。
random.poissonvariate(mu)#生成一个符合泊松分布的随机整数。
random.triangular(low, high=None, mode=None)#生成一个符合三角形分布的随机浮点数,范围在 [low, high] 之间,mode 指定峰值位置。
## 种子设置
random.seed()
random.getstate()#返回当前随机数生成器的状态。
random.setstate(state)#设置随机数生成器的状态为之前保存的状态。
## 其他实用工具
random.getrandbits(k) #生成一个 k 位的随机整数。
random.randbytes(n) #生成一个长度为 n 的随机字节串
Economics¶
经济实验方法:更接近自然科学 ¶
- 可控性 : 可在实验室中排除研究不关心的因素 ; 发现真正的因果关系
- 可重复性 : 实验可以在全世界范围内重复
基尼系数 ¶
# 1.公式法
def gini(L):
s1=0
s2=0
for i in L:
s2 += 2*len(L)*i #分母
for j in L:
s1 += abs(i-j) #分子 绝对值
return s1/s2
L=[]
for i in df.index:
L=L+[ df.loc[i,'income'] for j in range(int(df.loc[i,'people']/10000))]
gini(L)
#2.洛伦兹曲线(见画图)
s=0 #面积B(洛伦兹曲线内)
for i in df.index[1:]:
people1=df.loc[i-1,'洛伦茨曲线'] #梯形的下底
people2=df.loc[i,'洛伦茨曲线'] #梯形的上底
people_ratio=df.loc[i,'people_ratio']
s+=(people1+people2)*people_ratio*0.5
Gini=round((100*100*0.5-s)/(100*100*0.5),2)
Gini
股票相关 ¶
1. 夏普比率是诺奖获得者 Sharpe(1966) 开发的一种基金绩效评价标准。它通常用来衡量一项投资(如证券或投资组合)与无风险资产相比,在调整风险后的表现,其定义为投资回报与无风险回报之间的差额,除以投资回报的标准差,代表投资者每增加一个风险单位所获得的额外回报。
- 夏普比率 <0 时,资产表现还不如无风险利率,按大小排序没有意义
- 夏普比率 =0 时,资产平均净值和无风险利率一样,但承担了额外的风险
- 0< 夏普比率 <1,资产波动风险大于收益率。
- 夏普比率 =1,资产波动风险等于收益率,即投资风险每增加 1%, 预期收益也将增加 1%。
- 夏普比率 >1,资产波动风险小于收益。
- 夏普比率越高,那么就说明该资产每单位风险能够获得的风险回报也越高。
$$SharpeRatio= \frac{E(R_p)-R_f}{\sigma_p}$$
2. 单因子模型(见案例)
$$ r - R_f = \alpha + \beta ( K_m - R_f ) $$
- $r$:市场指数
- $R_f$:无风险收益率
- Sharpe(1964) 提出金融资产的收益拆分为两部分:
- $\alpha$:不跟随市场波动的收益,$\alpha$ 越大,资产组合的质量越好
- $\beta$:跟随市场一起波动的收益,牛市可选择 $\beta$ 大的资产组合,熊市则避之
3. 珐玛三因子模型(见案例) 法玛三因子模型(Fama-French Three-Factor Model)是一种资产定价模型(Capital Asset Pricing Model,CAPM)的扩展,用于解释股票回报的变异性。该模型由2013年诺奖获得者尤金·法玛(Eugene Fama)和肯尼斯·法rench(Kenneth French)于1992年提出。
- 三因子模型中的三因子分别指市场风险因子、市值因子和价值因子 , 这三个因子被认为是影响股票回报的重要因素。
- 市场风险因子指的是股票市场整体的风险,一般用大盘指数体现
- 市值因子是指公司的市值大小,根据市值效应(size effect,小市值公司和大市值公司收益率存在显著差异)
- 价值因子是指公司的估值水平,根据价值效应(Value effect,低市净率的公司和高市净率公司,其收益率存在显著差异)
$$ R_i - R_f = \alpha_t + \beta(MKT_i) +\gamma(SMB_i) + \lambda(HML_i) + \xi_i $$
$R_i$表示股票i的预期回报
$R_f$表示无风险收益率(当时间较短不变时,可与$\alpha_t$合并
$\beta$表示股票i的市场风险系数
$\gamma$表示股票i的市值因子系数
$\lambda$表示股票i的价值因子系数
$MKT$表示市场风险因子的回报率
$SMB$表示市值因子的超额回报
$HML$表示价值因子的超额回报
- 通过考虑市值和价值因子,法玛三因子模型可以更好地解释股票回报的变异性。该模型被广泛应用于投资组合管理、风险管理和资产定价等领域。 在此基础上,Carhart的四因子增加了动量因子(一直涨的和一直跌的股票收益率的差异),Fama五因子模型增加了盈利因子(净资产收益率)和投资因子(资产增长率)。一些扎根中国的研究Liu等(2019)认为,价值因子在中国应该采用市盈率。
4. 事件研究法(ESM
事件研究法(Event Study Method)用于评估某一事件的发生或信息的发布,是否会改变投资人的决策,进而影响股票价格或交易量的变化。事件研究法应用于金融领域,借助金融市场数据分析某一特定经济事件对该公司价值或市场产生的影响,即是否产生异常收益(Abnormal Returns
- ESM 与 DID 区别
- DID 是双重差分 (i. 事件前后差分;ii. 处理组和控制组之间的差分 );
- ESM 是一重差分 ( 事件前和事件后 ) + 模型方法替代处理组和控制组之间的差分,
- 如果研究中既有处理组又有对照组,那么 DID 是合适的研究设计;
- 如果只有处理组,没有完美对照组 , 但存在较理想的控制因素 , 且处理组的数据是高频时间序列数据,那么 ESM 则是合适的研究方法。
- 基本概念
- 事件日 (Event date):
- 特定事件发生的日期 ( $ t=0 $ ) 。
- 事件窗口期 (Event window) :
- 对事件涉及到的股票价格进行考察的时间段 ($[t_1,t_2]$ , 通常 $t_1<0$ 且 $t_2>0$) 。例如,如果某课题是研究企业兼并收购 (M&A) 对其股票价格的影响时,事件日期就是企业发布并购公告的当天,事件窗口从发布并购公告的当日开始,依据研究目的包含公告发布的前一天和公告发布的后一天。有时候也会包含事件发生的前后数日。
- 估计窗口 (Estimation window) :
- 估计窗口一般选择为事件发生前一段时间,通常是事件发生前 210 个交易日至前 11 个交易日 $[-210,-10]$ 。估计窗口与事件窗口不可有交集。
- 正常收益 (Normal Returns) :
- 为了考察特定事件对公司股价的影响,我们首先需要知道,如果没有该事件发生,股票收益率应该是多少,这也就是我们需要估计正常收益率的原因。
- 异常收益率 (Abnormal Returns, ARs):
- 每只股票实际收益率与正常收益率的差值,异常收益率能够反映该事件的经济影响。
- 累积异常收益率 (Cumulative abnormal returns,CARs) :
- 每只股票在事件窗口内异常收益率的简单加总,最常见的是 $CAR(-1,+1)$ 和 $CAR(-2,+2)$ ,正负的天数可根据研究需要进行相应调整。
- 事件日 (Event date):
- 事件研究法的基本步骤
- 定义事件
- 如公司的兼并收购行为 , 新产品的推出、不良宣传、或召回等
- 选择事件发生的日期窗口与估计窗口
- 一个事件往往有多个日期 , 信息的提前泄露会让研究者难以判断 , 一般采用第一个公开的日期
- 选取样本范围
- 样本的时间范围需要覆盖整个事件窗口
- 样本所覆盖的时间范围需要保证没有其他事件的干扰 ( 用更高频的数据解决需求不足的问题 )。
- 确定事件窗口 (event window)
- 事件窗口覆盖的时间也应该尽可能的缩短,来排除同时期其他事件的干扰
- 事件发生前的 90 天记作 -90 days, 事件发生后的 90 天记作 +90 days,那么该分析的事件窗口即为 [-90 days, 90 days]
- 确定估计窗口 (Estimation Window)
估计窗口早于事件窗口 , 不可与事件窗口有交集
利用估计窗口期的相关数据和选定的收益率模型估计相应的参数
- 模型通常为珐玛三因子模型
根据估计参数和事件窗口的数据来计算事件窗口期的预期收益率
- 计算 AR 和 CAR
- 期望股票收益率减去实际收益率,便得到了 AR; 将事件窗口内的 AR 累加,便得到了 CAR.
- 定义事件
#夏普
for code in 个股夏普率.index:
df12=df11[df11['证券代码']==code]
个股夏普率.loc[code,'夏普率']=(df12['考虑现金红利再投资的日个股回报率'].mean()
*交易日数-0.015)/(df12['考虑现金红利再投资的日个股回报率'].std()
*交易日数**0.5) #每年242个交易日
#计算多年涨幅
years=sorted(list(set(df["year"])))
for y in years:
df0=df[df["year"]==y].sort_values("trade_date")
mul=1
for i in df0.index:
mul=mul*(df0.loc[i,"pct_chg"]+100)/100
df1.loc[df1.shape[0]+1]={"年份":y,"涨幅":mul*100-100}
#按照涨幅复权价格
df1['price']=(df1['pct_chg']/100+1).cumprod()
#360日移动平均线
df1['360days_avg']=df1['price'].rolling(360,min_periods=10).mean() #对时间序列数据进行快速的移动平均计算
df1.plot(y=['price','360days_avg'])
js['市值']= js['总股数'] * js['日收盘价']
费雪方程式 ¶
df_demand["demand_nominal"]=df_demand["demand_reali"]+(100*inlfation-100)
统计 ¶
china["名义增长率"]=china["地区生产总值"].pct_change()*100
china["实际增长率"]=china["地区生产总值指数(上年=100)"]-100
#同比环比
#pct_change(12) 表示相对于12个月前的变化,计算同比;默认计算环比变化
df["M0环比"]=df["m0"].pct_change()
df["M0同比"]=df["m0"].pct_change(12)
#用环比算环比指数
for i in df2.index:
if i==0:
df2.loc[i,'房价100指数']=(df2.loc[i,'房价100环比']+100)/100
else:
df2.loc[i,'房价100指数']=(100+df2.loc[i,'房价100环比'])*df2.loc[i-1,'房价100指数']/100
时间序列 ¶
长期趋势时间序列
- 客观现象在一个相当长的时间内所呈现出来的持续性增加或减少的一种趋向和状态,如近 40 年来的经济增长
循环变动时间序列
- 循环变动是时间序列中的一种更长期的周期性波动,通常与经济和商业周期有关。循环性波动通常跨越数年,而不仅仅是每年。这种波动可能受到经济周期、投资周期、产业周期等因素的影响。循环性变动通常不像季节性变动那样具有明确的、规则的周期性,而是在更大的时间尺度上发生。
季节变动时间序列
- 季节变动是时间序列数据中具有明显的重复和周期性模式的变化。这种变化通常是由季节性因素引起的,如季节性销售波动、天气季节变化等。季节性模式通常是在一年内或更短时间内的周期性变化。例如,零售行业可能会在假日季节(如双 11 购物季节)期间经历销售增长,而冰箱、空调等冷却设备销售可能在夏季季节增加。季节变动通常是周期性的,因此可以在特定时间点或季节中预期。
其他周期性时间序列
- 其他周期性波动可能由不同的因素引起,例如每周的工作日和周末效应、特定行业的生产周期、定期的社会活动等。这些波动在时间序列分析中通常被视为特定周期内的变动,并且可能需要单独建模和处理。
非平稳时间序列
- 上述序列或者上述序列的组合称为非平稳时间序列,其统计性质在时间上发生变化。
平稳时间序列
- 平稳时间序列的统计性质在时间上保持不变,即没有趋势、循环、季节性或其他周期性变化,数据随机进行波动,是许多时间序列分析方法的基础。
无长期趋势,用同期平均消除干扰
- 如果在序列图中,没有发现时间序列包含长期趋势,则直接利用原序列进行同期平均和总平均,消除不规则变动,计算出季节指数。
有长期趋势,分离趋势和季节
- 计算每个周期内的平均值,以得到季节性成分
- 通过计算趋势线或移动平均线来估计时间序列的趋势成分
- 使用趋势 - 季节性成分去除后得到一年以上的明显波动,再分离循环成分
- 计算残差 - 无法由趋势、季节性、循环性和已知成分解释的时间序列的剩余部分
加法分解时间序列
- Value = 趋势 + 季节 + Error
- 如果时间序列的趋势是线性的,即趋势成分以固定的增长率或绝对增量变化,通常使用加法分解更合适
- 时间序列数据包含零值或接近零的值,可能更适合使用加法分解,以避免出现零除法错误
乘法分解时间序列 :
- Value = 趋势 x 季节 x Error
- 如果趋势是非线性的,特别是随时间增长率增加或减小,可能需要考虑使用乘法分解
- 如果数据始终为正且无零值,可以考虑使用乘法分解。
#季节趋势
import statsmodels.tsa.seasonal as sea
mul=sea.seasonal_decompose(df3['m0'], model='multiplicative', extrapolate_trend='freq')
add = sea.seasonal_decompose(df3['m0'], model='additive', extrapolate_trend='freq')
ARIMA 模型 ¶
ARIMA 模型(自回归积分滑动平均模型,Autoregressive Integrated Moving Average)是一种常用的时间序列预测模型,特别适用于对单变量非平稳时间序列的建模和预测。ARIMA 模型由三个主要部分组成:自回归 (AR)、差分 (I)、滑动平均 (MA),其目的是通过捕捉时间序列中的趋势、周期性及随机波动来生成预测。
- 自回归 (AR) 部分 $$Y_t = \phi_1 Y_{t-1} + \phi_2 Y_{t-2} + \dots + \phi_p Y_{t-p} + \epsilon_t$$
- 差分 (I) 部分 $$Y'_t = Y_t - Y_{t-1}$$
- 滑动平均 (MA) 部分 $$Y_t = \epsilon_t + \theta_1 \epsilon_{t-1} + \theta_2 \epsilon_{t-2} + \dots + \theta_q \epsilon_{t-q}$$
- ARIMA 模型的形式 ARIMA(𝑝,𝑑,𝑞),参数分别为:
- p 自回归阶数(前多少个时间点的值用于预测当前值)
- q 差分阶数(用于使时间序列平稳化)
- q 滑动平均阶数(多少个过去的误差项用于预测当前值
) 。 - ARIMA(1,1,1) 为 $$Y_t = \phi_1 Y_{t-1} + \epsilon_t + \theta_1 \epsilon_{t-1}$$
pmdarima 模块 ¶
- pmdarima 是一个基于 Python 的时间序列建模库,专门用于自动化 ARIMA 模型的构建和优化。
- 它可以在不需要手动调参的情况下快速搭建合适的时间序列预测模型。
#pmdarima 自动搜索 ARIMA 模型的最佳参数组合
from pmdarima import auto_arima # 需要将numpy降级 pip3 install "numpy<2"
import pandas as pd
import time
model = auto_arima(df['m0'][:279],seasonal=True, m=12, trace=True,error_action="ignore")
forecasts = model.predict(4)#预测值
df['m0'][279:]#实际值
Benford¶
- Benford's Law(本福德定律)是一种数学现象,它描述了在许多自然数据集中,数字 1 出现的概率明显高于其他数字(2、3、4、5、6、7、8、9)的概率。具体来说,根据 Benford's Law,数字 1 在数据集的首位出现的概率约为 30.1%,而数字 9 出现在首位的概率仅为 4.6% 左右,其他数字的概率依次递减。
- 这个现象最初由美国天文学家 Simon Newcomb 于 1881 年提出,后来由数学家 Frank Benford 于 1938 年进行了详细研究,因此得名 Benford's Law。这个定律的应用领域相当广泛,包括会计、审计、数据分析、金融领域、选举投票数据等各种领域。
- Benford's Law 的实际应用包括检测金融舞弊、分析选举投票数据的真实性、审计财务报表、识别假票据等。当数据集的分布不符合 Benford's Law 时,可能表明数据存在问题,需要进一步的调查和审查。这个法则在数字分布分析中具有重要的统计学和实际应用价值。
- 当数字 $d \in \{1,2,3,4,5,6,7,8,9\}$, 满足以下公式 $$ P(d)=log_{10}(d+1)-log_{10}(d)=log_{10}(\frac{d+1}{d}=log_{10}(1+\frac{1}{d}) $$
import numpy as np
benford= np.log10(1 + 1 / np.arange(1, 10))
截面数据 ¶
截面数据(cross-section data)是指在同一时间(时期或时点)截面上反映一个总体的一批(或全部)个体的同一特征变量的观测值,是样本数据中的常见类型之一。 横截面数据不要求统计对象及其范围相同,但要求统计的时间相同。 常见截面数据:问卷调查数据(单次);普查数据(单次)
不同的变量之间可以用不同单位来度量。如果出现度量的数值普遍过大或过小,会造成数据难以对比,回归系数不显著等问题。这时,可以采取去量纲处理。
#离差标准化
import pandas as pd
df=pd.DataFrame([1,9,1,2,2,3,4,5,6,500],columns=["data"])
x_max = df['data'].max()
x_min = df['data'].min()
df['离差标准化'] = (df['data'] - x_min) / (x_max - x_min)
#标准差标准化,标准差为1
x_mean = df['data'].mean()
x_std = df['data'].std()
df['标准差标准化'] = (df['data'] - x_mean) / x_std
df['变量中心化'] = (df['data'] - x_mean)
#小数定标标准化
import numpy as np
k_max = abs(df['data']).max()
k = np.ceil(np.log10(k_max))
df['小数定标标准化'] = df['data'] / 10 ** k
#取对数
df['取对数'] = df['data'].apply(np.log)
df['取对数+1']=df['data'].apply(lambda x:np.log10(x+1))
#取变量排序值
df["排序"]=df['data'].rank(method='average')
df.corr()
df.describe()
#多重共线性检验
X=df1[["除被解释变量外的变量列"]].copy()
X["常数项"]=1
vif=pd.DataFrame()
vif["features"] = X.columns
vif["vif factor"]=[statsmodels.stats.outliers_influence.variance_inflation_factor(X.values,i) for i in range(X.shape[1])]
vif
#=1没有多重共线性。1-5表示存在中度多重共线性;大于5表示存在严重的多重共线性。
简单回归 ¶
直接关系 调节关系 中介关系
因果关系
1. 排除结果发生在原因之前的情形
2. 排除原因和结果之间的偶然性
3. 排除第三个变量的影响
4. 排除逆向因果关系
相关关系
工具变量法
- 需要排除潜在的第三个变量
- 需要排除逆向因果
- 工具变量需满足
- 工具变量对原因有影响
- 工具变量对结果没有影响
- 不存在第四个变量同时影响结果和工具变量
import statsmodels.formula.api as smf
from linearmodels.panel import PanelOLS
from linearmodels import RandomEffects
import statsmodels as sm
#直接关系
results = smf.ols('Y ~ X1 + X2', data=df).fit()
print(results.summary())
results.params
results.resid
results.pvalues
results.rsquared
#OLS法二
exog_vars =['X1','X2']
exog = df[exog_vars].copy()
exog['常数项']=1
mod = PanelOLS(df['Y'], exog)
ols_res = mod.fit()
#调节关系
df=df.copy()
df['price1']=df['price']-df['price'].mean()
df['m_quanqiugou1']=df['m_quanqiugou']-df['m_quanqiugou'].mean()
df['price_quanqiu']=df['price1']*df['m_quanqiugou1'] #交叉项
results = smf.ols('Y ~ price1 + m_quanqiugou1 + price_quanqiu ', data=df).fit()
#随机效应
mod = RandomEffects(df['Y'], exog)
re_res=mod.fit()
#固定效应
mod = PanelOLS(df['Y'], exog,entity_effects=True)
fe_res = mod.fit()
#2SLS
reg4 = smf.ols('产权保护指数89_95 ~ 居民死亡率对数 ', data=df7).fit()
df7['预测产权保护指数4'] = reg4.predict()
reg7 = smf.ols('人均GDP95对数 ~ 预测产权保护指数4 ', data=df7).fit()
面板 ¶
1、 OLS 回归 pooled model
所有的组都相同。即同一个方程 ,截距项和斜率项都相同。 假设所有个体或组之间没有差异,所有的观测值都来自同一个总体。 适用于当数据中不存在组间异质性的情况。
$y_{it}=c+bx_{it}+\varepsilon_{it} \qquad c 与 b 都是常数 $
2、固定效应模型 fixed-effect model
$y_{it}=c+a_i+bx_{it}+\varepsilon_{it} \qquad cov(c,x_{it})\neq0$
固定效应方程隐含着跨组差异可以用常数项的不同刻画。每个 $a_i$,是每个个体或组特有的常数项,被视为未知的待估参数。$x_{it}$ 中任何不随时间推移而变化的变量都会模拟因个体而已的常数项。
适用于当不同个体或组之间存在系统性差异,并且这些差异与解释变量相关时。所有不随时间变化的变量都会被固定效应吸收,因此无法估计这些变量的影响。
3、随机效应模型 random-effects model
$y_{it}=a+u_i+bx_{it}+\varepsilon_{it} \qquad cov(a+u_i,x_{it})=0$
在随机效应模型中,分组的差异不再通过每组单独的常数项来刻画,而是通过引入一个随机变量来反映。这意味着不同组之间的差异不是固定的常数,而是被视为随机的、从某个总体分布中抽取的。
$a$ 是一个常数项,是不可观察差异性的均值,表示每个个体或组的随机偏差,假定从某个分布中抽取。$u_i$ 为第 $i$ 个观察的随机差异性,不随时间变化。
适用于当不同个体或组之间的差异可以视为随机的、独立于解释变量时。
相比固定效应模型,随机效应模型更有效率,因为它利用了更多的信息,但需要满足随机效应假设。
4、混合效应模型
$y_{ij} = \beta_0 + \beta_1 x_{ij} + u_{0j} + u_{1j} x_{ij} + \varepsilon_{ij}$
- $ y_{ij} $: 第 $ j $ 组中第 $ i $ 个观测值的因变量。
- $ \beta_0 $: 固定效应部分的截距项,即所有组共享的平均截距。
- $ \beta_1 $: 固定效应部分的斜率项,即所有组共享的解释变量 $ x_{ij} $ 对因变量的平均影响。
- $ x_{ij} $: 第 $ j $ 组中第 $ i $ 个观测值的解释变量。
- $ u_{0j} $: 随机效应部分的截距项的偏差,表示组 $ j $ 相对于整体截距 $ \beta_0 $ 的随机偏离。
- $ u_{1j} $: 随机效应部分的斜率项的偏差,表示组 $ j $ 的斜率相对于整体斜率 $ \beta_1 $ 的随机偏离。
- $ \varepsilon_{ij} $: 误差项,表示个体 $ i $ 在组 $ j $ 中的观测误差,通常假定为正态分布, $ \varepsilon_{ij} \sim N(0, \sigma^2) $。
适用于当数据同时包含固定效应和随机效应时,即既有跨组的系统性差异,也有组内随机波动。 允许估计组间差异的同时,也考虑了组内的随机变化,提供了更灵活的建模方式。
举例
- OLS 回归(Pooled Model)
研究全国范围内房价与人均收入的关系。
数据:收集了多个城市的数据,每个城市有若干年份的房价和人均收入。
假设:所有城市的房价与人均收入之间的关系是相同的,没有考虑城市间的差异。
公式:$y_{it} = c + b x_{it} + \varepsilon_{it}$,其中 $y_{it}$ 是第 $i$ 个城市第 $t$ 年的房价,$x_{it}$ 是第 $i$ 个城市第 $t$ 年的人均收入。
假设所有城市都遵循同一个线性关系,即房价对人均收入的反应相同。
如果实际上城市间存在显著差异(如一线城市和三四线城市的房价变化规律不同
) ,OLS 回归可能会产生偏差。
- 固定效应模型(Fixed-Effect Model)
研究不同公司在同一时间段内的利润率与广告支出的关系。
数据:收集了多家公司的年度财务数据,包括利润率和广告支出。
假设:每家公司都有其独特的特征(如公司文化、管理风格等
) ,这些特征会影响利润率,且这些特征不随时间变化。公式:$y_{it} = c + a_i + b x_{it} + \varepsilon_{it}$,其中 $a_i$ 是第 $i$ 家公司的固定效应。
每家公司的固定效应 $a_i$ 反映了该公司特有的影响因素。
例如,某些公司可能因为品牌优势或市场地位而有更高的利润率,即使广告支出相同。
固定效应模型可以控制这些未观察到的公司特定因素,从而更准确地估计广告支出对利润率的影响。
- 随机效应模型(Random-Effects Model)
研究不同医院的患者满意度与等待时间的关系。
数据:收集了多个医院的患者调查数据,包括患者的满意度评分和等待时间。
假设:不同医院之间存在随机差异,这些差异可以视为从某个总体分布中抽取,并且这些差异与等待时间无关。
公式:$y_{it} = a + u_i + b x_{it} + \varepsilon_{it}$,其中 $u_i$ 是第 $i$ 家医院的随机效应。
随机效应 $u_i$ 反映了不同医院之间的随机差异,如管理水平、设施条件等。
如果这些差异与等待时间无关,则可以使用随机效应模型来提高估计效率。
例如,某些医院可能因为地理位置或资源分配的不同而导致患者满意度的差异,但这些差异并不直接影响等待时间。
- 混合效应模型(Mixed-Effects Model)
研究不同学校的学生考试成绩与课外辅导时间的关系。
数据:收集了多所学校的学生成绩数据,包括学生的考试成绩和课外辅导时间。
假设:不同学校之间存在系统性差异(如师资力量、教学资源
) ,同时在同一所学校内,学生个体之间也存在随机差异。公式:$y_{ij} = \beta_0 + \beta_1 x_{ij} + u_{0j} + u_{1j} x_{ij} + \varepsilon_{ij}$,其中 $u_{0j}$ 和 $u_{1j}$ 分别是第 $j$ 所学校的截距和斜率的随机偏离。
固定效应部分 $\beta_0$ 和 $\beta_1$ 表示所有学校共享的平均截距和斜率。
随机效应部分 $u_{0j}$ 和 $u_{1j}$ 反映了每所学校相对于整体的随机偏离。
例如,某些学校可能因为更好的师资力量而有更高的平均成绩(截距偏离
) ,或者对课外辅导时间的反应更敏感(斜率偏离) 。混合效应模型允许我们同时考虑学校间的系统性差异和学校内的随机波动,提供更全面的分析。
豪斯曼检验
- 检验原理 豪斯曼检验的核心思想是比较固定效应模型和随机效应模型中解释变量的系数差异。如果这些差异显著,则说明固定效应模型更为合适;否则,随机效应模型可能是更好的选择。
原假设 ($H_0$):随机效应模型是合适的,即个体效应与解释变量不相关。 备择假设 ($H_1$):固定效应模型是合适的,即个体效应与解释变量相关。
- 检验步骤
使用固定效应模型估计参数 $\hat{\beta}_{FE}$。
使用随机效应模型估计参数 $\hat{\beta}_{RE}$。
计算两个模型估计系数的差异向量 $D = \hat{\beta}{FE} - \hat{\beta}{RE}$。
计算固定效应和随机效应模型估计系数的协方差矩阵之差: $ V = \text{Var}(\hat{\beta}{FE}) - \text{Var}(\hat{\beta}{RE}) $
构造豪斯曼检验统计量: $H = D' (V^{-1}) D $ 在原假设下,该统计量渐近服从卡方分布,自由度等于解释变量的数量。
如果 $H$ 统计量显著(即 p 值小于设定的显著性水平,如 0.05
import numpy as np
from scipy.stats import chi2
def hausman_test(fe_res, re_res):
# 提取系数
b_FE = fe_res.params
b_RE = re_res.params
# 系数的差异
beta_diff = b_FE - b_RE
# 协方差矩阵差异的逆矩阵
cov_diff = fe_res.cov - re_res.cov
cov_diff_inv = np.linalg.inv(cov_diff)
# 计算豪斯曼统计量
hausman_stat = beta_diff.T @ cov_diff_inv @ beta_diff
# 自由度为参数数量
df_hausman = len(b_FE)
# 计算 p 值
p_value = 1 - chi2.cdf(hausman_stat, df_hausman)
return hausman_stat, df_hausman, p_value #卡方,自由度,P值
hausman_test(fe_res, re_res)
if p_value < 0.05:
print("选择固定效应模型")
else:
print("选择随机效应模型")
#排版单列
def show_one(result,title=""):
df0=pd.DataFrame(columns=["变量",title])
list1=list(range(1,len(result.params)))+[0]
for i in list1:
beta=str(round(result.params.iloc[i],3))
p=result.pvalues.iloc[i]
star=""
if p <= 0.01:
star="***"
elif p <= 0.05:
star="**"
elif p <= 0.1:
star="*"
if hasattr(result,'bse'):
bse="("+str(round(result.bse.iloc[i],3))+")"
elif hasattr(result,'std_errors'):
bse="("+str(round(result.std_errors.iloc[i],3))+")"
df0.loc[df0.shape[0]+1]={"变量":result.params.index[i],title:beta+star+""+bse}
if hasattr(result,'resid'):
lens=len(result.resid)
elif hasattr(result,'resids'):
lens=len(result.resids)
df0.loc[df0.shape[0]+1]={"变量":"常数项",title:round(result.params.iloc[0],3)}
df0.loc[df0.shape[0]+1]={"变量":"样本量",title:lens}
df0.loc[df0.shape[0]+1]={"变量":"R平方",title:round(result.rsquared,3)}
#df0.loc[df0.shape[0]+1]={"变量":"x ",title:"x"}
return df0.set_index("变量")
#排版多个回归,先设置showone
def show_more(rs):
df0=pd.DataFrame()
for i in range(len(rs)):
df1=show_one(rs[i],"("+str(i+1)+")")
df0=pd.concat([df0,df1],axis=1,sort=False)
df0=df0.fillna("")
df1=df0.drop(["样本量","R平方","常数项"])
df1.loc["常数项",:]=df0.loc["常数项",:]
df1.loc["样本量",:]=df0.loc["样本量",:]
df1.loc["R平方",:]=df0.loc["R平方",:]
#df1.loc[df0.shape[0]+1]={"变量":"x ",title:"x"}
from IPython.display import display
display(df0.style.set_properties(**{
'white-space': 'pre-wrap',
}))
return df1
re_fixs=[]
for exog_vars in [exog_vars1,exog_vars2,exog_vars3,exog_vars4]:
exog = df[exog_vars].copy()
exog['常数项']=1
mod = PanelOLS(df['Log应用绩效'], exog,entity_effects=True)
re_fixs.append( mod.fit())
show_more(re_fixs)
羊群效应与搭便车效应 ¶
- 羊群效应(Herding Effect)
定义: 羊群效应是指个体在决策过程中,由于信息不对称或不确定性,倾向于模仿他人的行为或选择,而不是基于自己的独立判断。这种现象常见于金融市场、消费者行为和社会心理学等领域。
- 搭便车效应(Free-Rider Problem)
定义: 搭便车效应是指某些个体享受公共资源或集体行动带来的好处,但不承担相应的成本或责任。这种现象通常发生在公共物品或集体行动中,导致资源分配不公平或效率低下。
DID¶
$$Y_{it}=\alpha_0+\alpha_1du+\alpha_2dt+\alpha_3du\cdot dt+\varepsilon_{it} $$
其中,du 为分组虚拟变量,若个体 i 受政策实施的影响,则个体 i 属于处理组,对应的 du 取值为 1,若个体 i 不受政策实施的影响,则个体 i 属于对照组,对应的 du 取值为 0。dt 为政策实施虚拟变量,政策实施之前 dt 取值为 0,政策实施之后 dt 取值为 1。du·dt 为分组虚拟变量与政策实施虚拟变量的交互项,其系数 $\alpha_3$ 就反映了政策实施的净效应。
DID 必须满足以下两个关键条件:一是政策不能一次性全部铺开,必须存在一部分地区没有实施政策,而一部分地区实施了政策 ( 不然政策是一个常量,无法通过计量区分它带来的影响 );二是必须具有一个相应的至少两期(政策实施前后各一期)的面板数据集。
df['state']=df['code'].apply(lambda x:int(x[0]=='6'))
df['time']=df['time'].apply(lambda x:int(x>=201307))
随机对照试验 ¶
主要的思路是将研究对象随机分组,施加不同的干预措施,然后观察对比干预措施的效果如何。
- 随机对照实验最主要的贡献在于随机分组保证了控制和实验组除了干预措施以外没有其他统计学上的差异,因此实验后控制和对照组结果的不同是由于施加了不同的干预,由此实现对因果关系的识别。RCT 在医药领域应用非常广泛。假某公司开发了一种新的癌症药物 A,要想证明新药物 A 是否有效,可邀请了患有癌症的志愿者参加随机对照试验来比较治疗效果。通过抽签,公司将志愿者随机分配到实验组和对照组,然后为实验组 - 提供 A 药,对照组提供安慰剂。六个月后,对照两组的治疗效果便可以知道 A 药物是否有效。
- 随机对照实验已经成为经济学研究的主流方法之一。
- 2019 年,诺贝尔经济学奖共同授予了 Abhijit Banerjee,Esther Duflo 与 Michael Kremer 三人,以表彰他们采用随机对照实验对解决贫困问题做出的卓越贡献。在经济学研究中,随机对照实验已经广泛应用于研究教育、健康、消费、信贷等各种问题。
Pandas¶
二维表格 ¶
- 用行和列标识数据的表格称为二维表格
- 行为记录数或样本数
- 列为字段或变量
- 确保数据规范可用,需满足
- 行不能重复,需有行唯一标识
- 列不能重复,需有行唯一标识
- 每列中所有数据类型须一致
列结构:Series¶
Series:由一组数据以及一组与之相关的标签数据(即索引)组成
- 与一维数组与 numpy 中的 array 类似。
- 标签不能重复
与 Python 基本的数据结构 List 很相近,其区别是:
- List 中的元素可以是不同的数据类型
- Array 和 Series 中则只允许存储相同的数据类型,可以更有效的使用内存,提高运算效率。
与 Python 中的 Dict 的区别是:
- 可弥补 dict 不能排序的缺憾
以时间为索引的 Series,可以表示时间序列
DataFrame:二维的表格型数据结构。¶
- 可以将 DataFrame 理解为 Series 的容器。
- 要表示更高的维度,需要数据转换。
#文件处理
df=pd.read_csv(filename, encoding='gbk'/'UTF-8')
df=pd.read_table(filename, sep=',', encoding='gbk')#从限定分隔符,的文件或文本读取数据
df=pd.read_json(r'路径')
#{"name1":"rose1","name2":"rose2","name3":"rose3"}
#{"authors":[ {"name":"rose1"}, {"name":"rose2"}, ], "musicians":[ {"name":"rose1"} ] }
df=pd.read_excel(r'路径')
df=pd.read_feather(r'路径')
df.to_csv('1.csv')
df.to_excel('1.xlsx',index=False)
df.to_json('1.txt')
#基本操作
df=pd.DataFrame(columns=["A","B"])
df.head(num)
df.tail(num)
df.shape
df.shape[0] #行数,[1]列数
pd.set_option('display.max_rows')
pd.set_option('display.max_columns', 100)
df.values #不含行列索引的二维array
df.T
#缺失
pd.isnull(df)#判断数据是否为nan
df.isnull().any()#检查哪些列包含缺失值
df.isnull().sum()#统计各列空值
df[~df['A'].isnull()]#删除本列中空值的行
df[df['A'].isnull()]#仅保留本列中是空值的行
#修改
df.drop(0, axis=0)#去掉某行
df.drop('A', axis=1)#去掉某列
df.dropna()#删除所有包含空值的行
df.dropna(axis=1)#删除所有包含空值的列
df.append({"A":1,"B":1})#添加一行
df.loc[df.shape[0]+1]=d #添加一行d={"A":1,"B":1}
#行索引
df.index
df.set_index('A') #将A列设置为index
df = df.reset_index(drop=True) #重设index
df.rename(lambda x: x + 10)
#列索引
df.columns
df.rename(columns={'A':'a'},inplace=True)
df.rename(columns=lambda x: x + '_1')
#切片
df['列名1']#选取一列,得到Series格式
df[ ['列名1','列名2'] ]#选取多列,得到DataFrame格式
df[0:3] #选取行索引0,1,2这3条记录,也是DataFrame格式
df.loc[行标签,列标签], df.loc[行标签]#如果行标签和列标签都是列表,则得到DF格式,否则只有一个为值得Series,两者都为值则得到一个具体的值。
df.loc['001', :]#索引为'001'的行,所有列
df.loc[['001','003'], :]#索引为'001'或'003'的行,所有列
df2.loc[:, '姓名']#列索引为'姓名',所有行
df.iloc[行数值索引,列数值索引]#基本同df.loc,只是用索引数值进行引用
df.iloc[:3, :]#选取前3行
df.iloc[:, 0:3]#选取前3列
#筛选
df[(df.A >=1 ) & (df.B < 0) ]
df.loc[df2['A']>80,['B','C']]
df[df['D'].isin([0,9])]#选取 D 列中数为 0 和 9 的数
df.query('a > b') #即 df[df.a > df.b]
#排序
df.sort_values(by=['col1','col2'],ascending=True)#升序
#排名
df.rank(axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
'''axis:0 或 'index';1 或 'columns'
method:'average':如果多个元素具有相同的值排名平均值/'min'/'max'/'first': 按出现顺序;'dense': 与 'min' 类似,但排名是连续的。
numeric_only:True: 仅对数值列进行排名/False: 对所有列进行排名/None(默认): 根据数据类型自动判断。
na_option:'keep'(默认): 保留 NaN 值/'top': 将 NaN 值排在最前面。'bottom': 将 NaN 值排在最后面。
ascending:True(默认): 从小到大排名。False: 从大到小排名。
pct:True: 返回百分位排名。'''
#平移 多用于时间序列
df.shift(periods=1, freq=None, axis=0, fill_value=None)
'''periods:整数,表示要平移的周期数。正值表示向下或向右平移,负值表示向上或向左平移。
freq:日期偏移量,用于时间序列数据。例如,'D' 表示按天偏移,'M' 表示按月偏移。
axis:整数或字符串,表示沿哪个轴进行平移。0 或 'index' 表示沿行方向平移,1 或 'columns' 表示沿列方向平移。
fill_value:用于填充平移后空缺位置的值。默认为 NaN。'''
#分割列
df1['time']=df1['month'].apply(lambda x:str(x)[:10])# 伪字符串转字符串
df2 = df['姓名'].str.split('-',expand=True)
df2.columns = ['学号','姓名']
#找最小值
df[df["x"]==df["x"].min()]
#跨行计算
df['A'].cumsum 样本值累计和
df.cummin,cummax 样本值累计最大值和累计最小值
df.cumprod 样本值累计积
df.diff 计算一阶差分(对时间序列很有用)
df.pct_change 计算百分数变化
#分组
df.groupby(列名).sum() #分组求和
df.groupby(['列名1','列名2']).count() #分组计数
df.groupby(['列名1','列名2']).mean() #分组求均值
df2.groupby('省份')['B'].agg([np.sum, np.mean])#按省份分组计算B总分和平均分
df2['平均分'] = df2.groupby('省份')['A'].transform('mean')
df.groupby(列名).apply(自定义函数) #自定义函数的参数为列表
#合并
df3=pd.concat([df1,df2])#竖着拼,列名一致
df4=pd.concat([df41,df51],axis=1)#共同索引
df4=pd.merge(df1,df2,how="left",on=['A','B'])
#取对数
df[列].apply(lambda x: np.log(x))
时间处理 ¶
df['date'] = pd.to_datetime(df['date'],format='') #字符串转换为日期格式
df['days_interval'] = df['date'].diff().dt.days # 计算相邻日期的天数差
画图 ¶
%matplotlib inline
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['font.serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus']=False
plt.rcParams["figure.dpi"] = 300
df.plot(y='A',title='B',kind='scatter/bar/line',ax=plt.gca())
plt.savefig('filename.png', dpi=300, format='png') #保存为 png格式
df.plot(y=['列1','列2'],figsize=(10,5), title="标题")
#x只能一个,但y可以指定多个,多个y一般需要在一个数量级上,否则需要建立双坐标
df.plot(secondary_y=['列2']) #双坐标体系
fig,ax = plt.subplots(1,2, figsize=(10, 5))#双子图
df3.sort_values('A',ascending=False).head(10).plot(y=['A','B'],kind='bar',ax=ax[0])
df3.sort_values('B',ascending=False).head(10).plot(y=['A','B'],kind='bar',ax=ax[1])
import seaborn as sns
sns.set(rc={"figure.dpi":300, 'savefig.dpi':300})
sns.set_context('notebook')
sns.set_style("ticks")
sns.pairplot(df1)
sns.lmplot(x='price',y='sales',hue='m_quanqiugou',data=df)
sns.barplot(x="",y="",data=df)
sns.boxplot(x='', y='', data=df)
洛伦兹曲线 ¶
import matplotlib as mpl
## 先累计再算比重
df['all_income']=df['people']*df['income']#每类人群的总收入
df['people_cum']=df['people'].cumsum() #累积人口(总人口)
df['people_ratio']=df['people']/df['people'].sum()*100
df['people_ratio_cum']=df['people_cum']/df['people_cum'].max()*100
#最后累积总数
df['all_income_cum']=df['all_income'].cumsum()
df['洛伦茨曲线']=df['all_income_cum']/df['all_income_cum'].max()*100
df['绝对平均线']=df['people_ratio_cum']
## 先比再累积
df2011['收入占比']=df2011['家庭年收入']/df2011['家庭年收入'].sum()
df2011['洛伦茨曲线']=df2011['收入占比'].cumsum()
df2011['人数占比']=1/df2011.shape[0]
df2011['人数累计占比']=df2011['人数占比'].cumsum()
df2011['绝对平均线']=df2011['人数累计占比']
df.plot(x='people_ratio_cum', y=['洛伦茨曲线','绝对平均线'])
网络爬虫 ¶
Online 是大数据区别于小数据主要特征之一
数据格式
- 二进制格式(私有格式
) :xlsx - 文本格式(公开格式
) :csv/txt/json/api 接口 / 无固定格式 /html
网页数据展示特点
- 主要为人观看服务
- 数据背后夹杂的大量的无用信息
- 没有标准的格式
网络爬虫的流程
- 找到 HTML 网页源代码
- 分析代码结构,找出数据部分与其他无用部分区分标志
- 利用区分点设计区分模板(关键步骤)
- 用 python 获得整个网页源代码,运行区分模板代码提炼数据
HTML 文档基本概念
- 元素(标签
) :页面中的构建块,如 < div >、< p > 等 - 属性:如 id、class 等,提供额外信息
- 内容与空内容:元素内的实际内容,有些元素没有内容(如 < img >)
- 容器:嵌套其他元素的标签,如 < div >
- 外部资源:引用外部的 CSS、JS 文件
- 元素闭合规则:元素应闭合,如 < p >< /p >
常见 HTML 容器元素
- 块元素:如 < div >,独占一行,宽度默认为父元素的 100%,通常用于较大的结构划分
- 内联元素:如 < span >,独占一行,宽度默认为父元素的 100%
说明性元素
- 注释 < !-- -- >
- meta 提供元数据
- link 链接外部资源
- script 嵌入 JavaScript 代码
功能性元素
- 链接 a < a href=" 地址 " > 链接显示的内容 < /a >。
锚链接:用于页内定位,< a href="#xxx" >链接显示的内容< /a >,需要页面中存在 id="xxx" 的元素。
常用属性:target="_blank":在新窗口打开链接
- 图片 img < img src=" 图片地址 " alt=" 图片的文本说明 " width= height= / >
图片相关知识
- 矢量图:基于数学公式,缩放不失真,适合图标和图形。
- 位图:基于像素,缩放可能导致失真,适合照片和复杂图像。
- 像素:图像的基本单位。
- GIF:最高8位,支持动画,透明背景,适合图标。
- JPG:最高32位以上,适合风景。
- PNG:结合了GIF和JPG的优点,更适合网络环境。
- 段落 p:< p > 内容 < /p >
- 标题 h1-h7:< h1 > 内容 < /h1 > 到 < h6 > 内容 < /h6 >
- 表格 table, tr, td
标题 1 标题 2 内容 1 内容 2
- 列表 ul, ol, li
- 定义列表 dl, dt
- < dl>标记定义了一个定义列表,
- < dt>定义术语的标题
- < dd>定义术语的描述
- 定义
- 描述 1
- 描述 2
文本格式化
<b>加粗;strong加粗强调 <i>斜体;<em>斜体强调 <cite>标记标题,斜体
内嵌框架
<iframe>用于在当前网页中嵌入另一个网页或内容。创建了一个内联框架,可在其中加载外部资源,如网页、视频、地图等。
src:指定要嵌入的内容的URL。
width 和 height:设置框架的宽度和高度。
frameborder:控制边框显示(0为无边框,1为有边框)。
allowfullscreen:允许全屏模式。
loading:设置懒加载(lazy 或 eager)。
- 表单 form, input, textarea, select:用户与服务器交互的最主要的渠道之一,尤其是文件上传,用户登录等场合。
主要属性:
- id:唯一标识元素。
- class:为元素指定一个或多个类。
- title:提供元素的额外信息。
- style:定义元素的样式。
- width、height:定义元素的尺寸。
urllib 是 python 的一个获取 url(Uniform Resource Locators,统一资源定位符 ),可以用来抓取远程的网页。
urllib.request.urlopen(url, data, timeout) 可以获取页面,获取页面内容的数据格式为 bytes 类型,需要进行 decode() 解码,转换成 str 类型。
url : 需要打开的网址 data : 传递的参数,字典形式,默认为None时是GET方法,data不为空时, urlopen()的提交方式为POST timeout : 设置网站访问的超时时间,默认可省略 获取页面内容的数据格式为bytes类型,需要进行decode()解码,转换成str类型。
GET VS POST
爬虫获取数据除了 GET 方法 , 还有 POST 方法 .
GET直接获取数据,所有访问者获得同样的数据. GET携带的数据要通过链接体现,拼接的过程是?变量1=值1&变量2=值2 POST需要提交信息后获取数据(例如访问者提交查询信息后获得不同的数据) POST方法需要携带Data,为Dict(Json)格式
伪装为浏览器:定制网页请求信息的 header 主要属性
爬虫识别系统,一旦被识别为爬虫,封 IP 等方式,禁止继续访问。 - 需要针对性的做一些反爬虫策略。
反爬虫系统往往会根据 header 的信息判断是否为爬虫,因此可以伪装为桌面浏览器一致的信息,
User-Agent:浏览器名与版本号等 Host:服务器站点名 Referer:上一页的地址 Cookie:包含用户名登录,一些安全认证等信息
from urllib import request, parse
import time
import random
#函数化处理GET
def get_url(url):
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \n
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 \n
Safari/537.36'
req = request.Request(url, headers=head)
response = request.urlopen(req)
html=response.read()
string=html.decode('utf8')
time.sleep(random.random()*2) #平均间隔1秒,避免过度查询
return string
#函数化处理POST
def post_url(url,data):
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \n
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 \n
Safari/537.36'
data = parse.urlencode(data).encode('utf-8')
req = request.Request(url,data=data, headers=head)
response = request.urlopen(req)
html=response.read()
string=html.decode('utf-8')
time.sleep(random.random()*2) #平均间隔1秒,避免过度查询
return string
机器学习 ¶
机器学习的目标是从数据中学到模式(黑箱)并做出预测,而不是通过明确的编程(白箱,即计量经济模型)来实现特定的任务。
- 准备数据
- A 部分为一一对应的 X -> Y,B 部分为只有 X 没有 Y(需要预测)
- 模型的选择
- 选取通用模型(回归、决策树、支持向量机、神经网络等)
- 模型训练
- 将数据 A 分为 2 部分,A1 和 A2
- 使用 A1 数据,将模型参数调整到最佳状态 训练集
- 模型评估
- 使用 A2,对模型效力进行评估 测试集
- 预测
- 对数据 B 预测,预测 Y
文本情感分析:又称意见挖掘、倾向性分析等。简单而言 , 是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程。
- NLP(Natural Language Processing), 即自然语言处理是人工智能领域一个重要的分支,主要分析人类语言的人工智能。目前语音识别、机器翻译、聊天机器人、分词、情感分析是该领域主要代表性的细分领域。因此,情感分析工具通常包含在 NLP 软件包内。
- 英文 NLP 处理最常用的 Python 模块是 Nltk, 但不支持中文。当前最易于使用的中文模块是 snownlp。
文本分析 ¶
''' SnowNLP'''
s = SnowNLP(u'xxxx')
print(s.sentiments)
from snownlp import SnowNLP
import pandas as pd
mix=pd.read_csv('/home/public/Data/mix.csv',encoding='gbk')
mix=mix.dropna()
mix
#测评
right=0
wrong=0
for i in mix.index:
txt=mix.loc[i,'txt']
fen=mix.loc[i,'fen']
s = SnowNLP(txt)
f=s.sentiments #打分
print((f,fen))
if f>0.5 and fen>=3 or f<0.5 and fen<2.5:
right += 1
else:
wrong += 1
print("机器评分正确率为:",right/(right+wrong))
# 训练保存
sentiment.train('/home/public/Data/low.txt',
'/home/public/Data/high.txt')
sentiment.save('/home/public/Data/douban.marshal')
sentiment.load('/home/public/Data/douban.marshal')
图像数据处理 ¶
- 根据 Mehrabian(1971) 的结论,文字传递占信息传递的 7%,肢体语言(手势和面部表情)占 55%,声音占 38%。
- 图像数据的特点是数据的价值密度很低 , 识别复杂度高,因此反爬系统等人机识别常常使用包含验证码图像对人进行甄别。
- 包含经济含义的图像往往并不多见,但有人脸的图像(面部表情)很有可能包含一些信息。
# 人脸识别cv2模块
#图片中的人脸识别 pip3 install opencv-python ipywidgets
%matplotlib inline
import cv2
from matplotlib import pyplot as plt
import ipywidgets as widgets
from IPython.display import display
import os
img = cv2.imread('/home/public/Data/nju1.jpg')
case = cv2.CascadeClassifier()
case.load('/home/public/Data/haarcascade_frontalface_default.xml')
faces = case.detectMultiScale(img)
print(faces)
#使用detectMultiScale方法检测图像中的人脸,
#返回值是一个矩形列表,每个矩形表示一个人脸的位置和大小
sampleNum=0
for (x, y, w, h) in faces:
sampleNum+=1
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 255), 2)
#对于每个检测到的人脸,在原图上绘制黄色矩形框
cv2.imwrite("/home/public/data/face"+str(sampleNum) +
".jpg", img[y:y + h, x:x + w])
#截取的人脸区域保存为新的图像文件。
show_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #从BGR颜色空间转换为RGB
plt.imshow(show_img)
复杂网络分析 ¶
复杂网络是一种由大量相互连接的节点和边组成的网络结构,具有一些特殊的性质和行为。
- 节点(Nodes
) : 复杂网络由许多节点组成,有时也称为顶点。节点可以代表各种实体,如人、物体、计算机等,取决于网络的应用领域。 - 边(Edges
) : 节点之间的连接线被称为边,它表示节点之间的关系或相互作用。边可以是有向的(箭头表示方向)或无向的(没有方向) 。 - 度(Degree
) : 节点的度是指与该节点直接相连的边的数量。在有向网络中,分为入度和出度,分别表示指向该节点的边的数量和由该节点指向其他节点的边的数量。 - 邻居(Neighbors
) : 与某一节点直接相连的节点被称为邻居。节点的邻居可以用于分析节点的影响力和网络的结构。 - 路径(Path
) : 两个节点之间的路径是指通过边连接的节点序列。路径的长度是指路径上的边的数量。 - 直径(Diameter
) : 网络的直径是指网络中最长路径的长度。它反映了网络中信息传播的最远距离。 - 小世界现象(Small World Phenomenon
) : 复杂网络中的小世界现象表现为即使在大规模网络中,任意两个节点之间的路径长度相对较短。这意味着网络中的节点之间通过较少的中间节点就能相互连接。 - 群聚系数(Clustering Coefficient
) : 衡量网络中节点聚集程度的指标。节点的群聚系数表示该节点的邻居之间存在连接的概率。全局群聚系数是网络中所有节点群聚系数的平均值。 - 无标度网络(Scale-Free Network
) : 具有无标度特性的网络是指网络中只有少数节点具有极高的度,而大多数节点的度相对较低。这种网络结构在描述许多实际系统中的节点连接方式时很常见。 - 社区结构(Community Structure
) : 复杂网络中的社区是指网络中具有紧密连接的节点组。社区结构可以揭示网络内部的组织和功能分区。
研究方向
- 网络结构
研究网络的拓扑结构、度分布、社区结构等特征,以及构建和分析不同类型网络的模型。
无标度网络、小世界网络等模型是研究网络结构的经典方向
无标度网络理论主要关注节点度的分布特性。该理论表明,许多真实网络的度分布呈现幂律分布,即只有少数节点具有极高的度,而大多数节点的度相对较低。 小世界网络理论强调了网络中短路径和高聚类性之间的平衡关系。Watts和Strogatz提出的小世界模型是一个典型的例子,展示了在网络中通过增加少量的边,可以显著降低平均最短路径。
- 网络传播
- 研究信息、疾病、意见等在网络中的传播过程。这包括病毒传播模型、信息扩散模型、谣言传播等。
- 研究网络中的动力学过程,包括演化博弈、信息传播、危机传播等
数据结构
- 邻接矩阵(Adjacency Matrix)
- 邻接矩阵是一个二维数组,其中元素
A[i][j]
表示节点i
和节点j
之间是否有边。如果有边,则该元素通常为 1;否则,为 0。对于无向图,邻接矩阵是对称的。 - 例如,下图表示四个节点之间的连接关系(无向图
) : - 0 1 1 0
- 1 0 1 1
- 1 1 0 1
- 0 1 1 0
- 邻接矩阵是一个二维数组,其中元素
- 邻接表(Adjacency List)
- 邻接表是一种以列表形式表示节点和其邻居的数据结构。对于每个节点,都有一个关联的邻居列表。这种数据结构更适用于稀疏图,因为它只存储有边的节点。
- 例如,可用下面矩阵示了四个节点和它们的邻居(无向图
) : - 0: 1, 2
- 1: 0, 2, 3
- 2: 0, 1, 3
- 3: 1, 2
- 边列表(Edge List)
- 边列表是一种简单的数据结构,其中每个元素表示一条边。对于无向图,边可以由两个节点的编号组成。
- 例如,下列表表示一个五条边
- (0, 1), (0, 2), (1, 2), (1, 3), (2, 3)
- 关联矩阵(Incidence Matrix)
- 关联矩阵是一个二维数组,其中行代表节点,列代表边。如果节点与边相连,则对应元素为 1,否则为 0。对于有向图,入边为 -1,出边为 1。
- 例如,下列表示一个三个节点和四条边的有向图:
- -1 1 0 0
- 1 -1 1 -1
- 0 0 -1 1
常见复杂网络(见 14 讲)
分类
根据结构分类:
- 无标度网络(Scale-Free Networks): 其度分布呈幂律分布,即少数节点具有极高的度,而大多数节点的度相对较低。 - 小世界网络(Small-World Networks): 具有较短的平均最短路径和较高的聚类系数,即节点间存在短路径和紧密聚集的性质。 - 随机网络(Random Networks): 具有较均匀的节点度分布和较低的聚类系数,其结构类似于随机图。 - 中心边缘网络(Core-Periphery Network):网络被划分为中心部分和边缘部分,中心部分的节点之间连接较为密集,而中心和边缘之间的连接较为稀疏。 - 多层网络(Multilayer Networks): 包含多个层次,每个层次有不同类型的节点和边,节点可以在不同层次之间互相连接。
其他特殊结构网络
- 环形网络:环形网络是一种简单的拓扑结构,其中每个节点与两个相邻的节点相连,形成一个封闭的环。在环形网络中,每个节点的度为2。
根据动态行为分类:
- 动力学网络(Dynamic Networks): 网络结构随时间演变,节点和边的加入、删除或权重的变化使得网络的拓扑结构不断变化。 - 同步网络(Synchronized Networks): 研究节点间的同步现象,关注节点状态的协同演变。 - 传播网络(Spreading Networks): 研究信息、疾病、意见等在网络中的传播过程。
根据功能分类:
- 生物网络: 包括生物分子相互作用网络、神经网络等,用于研究生物系统内部的结构和功能。 - 社交网络: 描述个体或组织之间的关系,包括在线社交媒体、合作网络等。 - 信息网络: 包括因特网、通信网络等,用于传输和分享信息。 - 技术网络: 包括电力网络、交通网络等,用于管理和控制相应的技术系统。 - 金融网络: 描述金融市场中各个金融机构之间的关系,用于研究市场的波动和风险传播。
根据层次分类:
- 单层网络(Single-Layer Networks): 指一个网络中只有一个类型的节点和边。 - 多层网络(Multilayer Networks): 包含多个层次,每个层次有不同类型的节点和边,节点可以在不同层次之间互相连接。
根据拓扑特性分类:
- 连续网络(Continuous Networks): 节点之间的连接是连续的,通常用于描述物理空间中的网络。 - 离散网络(Discrete Networks): 节点之间的连接是离散的,通常用于描述虚拟关系或抽象网络。
中心度:网络中一节点在整个网络中所在中心的程度
度中心度:用度来衡量节点 v 中心度,直接数值是就是度的值 $deg(v)$,总节点数量为 $n$,则标准化的度中心度为 $\frac{deg(v)}{n-1}$。
- 强调某节点的单独价值。
接近中心度:接近中心度度量一个点和所有其他点的接近程度。首先计算一个点到其他所有点的距离的总和,然后将总和取倒数 $$\frac{1}{\Sigma_y {d(i,j)}}$$ 最后进行归一化处理得到一个介于 0-1 之间的数,定义为接近中心度。$$\frac{{n-1}}{\Sigma_y {d(i,j)}}$$
- 强调节点在网络中的价值,值越大,越处于中心的位置
中介中心度:表示该点的“中间人”程度,即媒介程度。经过一个点的最短路径的数量越多,就说明它的中介中心度越高。计算网络中任意两个节点的所有最短路径,如果这些最短路径中有很多条都经过了某个节点,那么就认为这个节点的中介中心度高。具体为:$$ \Sigma_{s\not=v\not=t} \frac{\delta_{st}(v)}{\delta_{st}} $$ 对于无向图 $$ \Sigma_{s<t} \frac{\delta_{st}(v)}{\delta_{st}} $$ 其中 $\delta_{st}$ 表示节点 $s$ 和 $t$ 之间最短路径的数量,$\delta_{st}(v)$ 表示 $s$ 和 $t$ 的最短路径经过节点 $v$ 的数量,
- 强调点在其他点之间的调节能力,控制能力
特征向量中心度:$$x_v = \frac{1}{\lambda} \Sigma_t{\alpha_{v,t}x_t} $$ 其中,如果 v 与 t 连接 $\alpha_{v,t}=1 $,否则为 0;$\lambda$ 为节点的邻接矩阵的特征值。
- 强调节点在网络中的价值,节点的价值是由临近节点的价值所决定的。与你连接的人越重要,你就越重要
群聚系数:用来网络的节点之间结集成团的程度的系数,用一个节点的邻接点之间相互连接的程度来表示。在社交网络中,度量某个节点朋友之间相互认识的程度。群聚系数表示为:$$ c_u=\frac{2T(v)}{deg(v)(deg(v)-1)}$$ 其中 $T(u)$ 表示网络中经过 $v$ 的三角形的数量,$deg(v)$ 表示 $v$ 的度数。
- 群聚系数强调节点间凝聚成团的程度
import networkx as nx
import matplotlib.pyplot as plt
import random
## 无向图
G = nx.Graph()
G.add_nodes_from([1, 2, 3, 4,5])
G.add_edges_from([(1, 2), (2, 1), (2, 3),(2,4),(4,5)])
nx.draw(G,with_labels=True,node_color='red',
font_size =25, node_size=1000)
##有向图
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3, 4,5])
G.add_edges_from([(1, 2), (2, 1), (2, 3),(2,4),(5,2)])
nx.draw(G,with_labels=True,node_color='red',
font_size =20, node_size=500)
## 随机生成网络,不允许自指向,20节点,60条边
G = nx.DiGraph()
nodes=range(1,21)
G.add_nodes_from(nodes)
edges=[]
for i in range(61):
x=random.choice(nodes)
y=random.choice(list(set(nodes)-set([x])))
edges.append((x,y))
G.add_edges_from(edges)
nx.draw(G,with_labels=True,node_color='red',font_size =15, node_size=500)
## 随机,不允许自指向,20节点,每个节点关注3个邻居
G = nx.DiGraph()
nodes=range(1,21)
edges=[]
for i in nodes: #为每个节点随机选择三个不同的节点并生成边
x1=random.choice(list(set(nodes)-set([i])))
x2=random.choice(list(set(nodes)-set([i,x1])))
x3=random.choice(list(set(nodes)-set([i,x1,x2])))
edges.append((i,x1))
edges.append((i,x2))
edges.append((i,x3))
G.add_edges_from(edges)
pos=nx.shell_layout(G)
nx.draw(G,pos,with_labels=True,node_color='',
font_size =15, node_size=500)
## 中心-边缘网络
G1 = nx.DiGraph()
nodes=range(1,21)
nodes1=list(range(1,11))#中心节点
nodes2=list(range(11,21))#边缘节点
edges=[]
for i in nodes1: #中心节点,只关注中心节点
x1=random.choice(list(set(nodes1)-set([i])))
x2=random.choice(list(set(nodes1)-set([i,x1])))
x3=random.choice(list(set(nodes1)-set([i,x2,x1])))
edges.append((i,x1))
edges.append((i,x2))
edges.append((i,x3))
for i in nodes2: #边缘节点,关注中心节点
x1=random.choice(list(set(nodes)-set([i]+nodes2)))
x2=random.choice(list(set(nodes)-set([i,x1]+nodes2)))
x3=random.choice(list(set(nodes)-set([i,x2,x1]+nodes2)))
edges.append((i,x1))
edges.append((i,x2))
edges.append((i,x3))
G1.add_edges_from(edges)
pos=nx.shell_layout(G1)
nx.draw(G1,pos,with_labels=True,node_color='red',
font_size =15, node_size=500)
## 环形网络,每个节点关注后面3个其他节点,均匀对称
G2 = nx.DiGraph()
nodes=range(1,21)
edges=[]
for i in nodes: #中心节点,只关注中心节点
edges.append((i,(i)%len(nodes)+1))
edges.append((i,(i+1)%len(nodes)+1))
edges.append((i,(i+2)%len(nodes)+1))
G2.add_edges_from(edges)
pos=nx.circular_layout(G2)
nx.draw(G2,pos,with_labels=True,node_color='red',
font_size =15, node_size=500)
## 中心度
df=pd.DataFrame(nx.degree_centrality(G1), #同理查看其他中心度
index=["度中心度"]).T.sort_values("度中心度",
ascending=False)
nx.degree_centrality(G1)#度中心度
nx.in_degree_centrality(G1)#入中心度
nx.out_degree_centrality(G2)#出中心度
nx.closeness_centrality(G1)#接近中心度
nx.betweenness_centrality(G1)#中介中心度
nx.eigenvector_centrality(G1)#特征向量中心度
nx.clustering(G1) #群聚系数
len(G.edges())#边的总数,当网络可变时,可衡量网络效应大小