Learn how to manipulate time series data with pandas and conduct significance testing through simulation using Python, to analyze stock market volatility.
Ekim tarihsel olarak hisseler için en oynak aydır; peki bu kalıcı bir sinyal mi, yoksa verideki gürültü mü?
"Son 32 yılda, ortalama olarak S&P500 için en oynak ay Ekim, en az oynak ay ise Aralık olmuştur".
Bu derste, Python kullanarak bu olgunun baştan sona analiz ve testini yaparak istatistiksel olarak anlamlı olup olmadığını belirleyeceğiz.
Veriyi canlandıracak bir forma dönüştürmek için Resample gibi pandas zaman serisi işlevlerini kullanarak ham hisse fiyatı verilerini düzenleyeceğiz.
Ardından, hantal formüller yerine Python ile simülasyon kullanarak hipotez testinin nasıl yapılacağını keşfedeceğiz.
Son olarak, istatistiksel analizde büyük bir sorun olan Çoklu Karşılaştırma Yanlılığı'nı ele alacak, bununla nasıl başa çıkılacağını öğrenecek ve matplotlib ile görsel olarak 'p-Hacking' etkisini göstereceğiz.
Hedefimiz:
Zaman Serilerini analiz etmek için Pandas'ı nasıl kullanacağınızı göstermek
Bir hipotez testinin nasıl kurulacağını anlamak
Hipotez testini gerçekleştirmek için Python ile simülasyon kullanmak
Çoklu karşılaştırma yanlılığını hesaba katmanın önemini göstermek
Verimiz:
Bu analiz için Günlük S&P500 verisini kullanacağız; özellikle, 1986'dan 2018'e kadar olan ham günlük kapanış fiyatlarını kullanacağız (şaşırtıcı şekilde bulması zor olduğundan, herkese açık hale getirdim).
Bu yazının ilhamı, burada 87 yıl yerine 32 yıllık veriyle yeniden üreteceğimiz Winton çalışmasından geldi.
Pandas ile Düzenleme
Bazı aylarda görülen aşırı oynaklığın gerçekten anlamlı olup olmadığını ve dolayısıyla devam etme olasılığını yanıtlamak için, 32 yıllık fiyat verimizi incelediğimiz olguyu ortaya koyan bir formata dönüştürmemiz gerekiyor.
Seçtiğimiz format ortalama aylık oynaklık sıralaması (AMVR) olacak.
Aşağıdaki kod, ham fiyat verimizi bu formata nasıl getirdiğimizi gösteriyor. Hadi başlayalım!
Önce standart içe aktarmalar. (matplotlib.patches, histogramdaki tek tek çubukların stilini kontrol etmemizi sağlar)
#standard importsimport numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
%matplotlib inline
Altyapı:
Jupyter not defterlerinde grafikleri tam genişlikte göstermek için güzel bir numara:
#resize charts to fit screen if using Jupyter Notebook
plt.rcParams['figure.figsize']=[15,5]Altyapı:
Bu opsiyonel — denemekten çekinmeyin, ne olacağını görün!
Verimizi, yol parametresi alan read_csv yöntemiyle içe aktaralım; bizim durumumuzda veriye giden bir url. Ayrıca, indekste 'date'i kullanmasını ve verilen metinden tarihleri elinden geldiğince otomatik ayrıştırmasını söylüyoruz.
#Daily S&P500 data from 1986==>
url ="https://raw.githubusercontent.com/Patrick-David/Stocks_Significance_PHacking/master/spx.csv"
df = pd.read_csv(url,index_col='date', parse_dates=True)#view raw S&P500 data
df.head()Altyapı:
close
date
1986-01-02
209.59
1986-01-03
210.88
1986-01-06
210.65
1986-01-07
213.80
1986-01-08
207.97
Bu bize S&P500'ün (SPX) ham, düzeltilmemiş kapanış fiyatlarını verir. Şimdi ham fiyatlarımızı günlük yüzde getirilerine dönüştürmemiz gerekiyor. Bunu yapmak için iki seçeneğimiz var:
Fiyatların doğal logunu alabiliriz. Bu, gerçek günlük getirilere bir yaklaşım verir.
Günlük yüzde değişimi doğrudan hesaplamak için pandas'ın 'pct_change()' yöntemini kullanabiliriz.
Amacımız için, bu veri seti boyutunda (8000'in üzerinde değer) pandas hesabı anında yapabildiğinden yöntem 2'yi kullanacağız. Ayrıca, bir önceki güne göre fiyat değişimi olmadığı için 'NaN' olan ilk değeri düşerek verileri temizleyeceğiz. pct_change() opsiyonel 'periods' parametresi alır ve dönem kaymasını değiştirir. Bunu varsayılan birde bırakacağız.
#To model returns we will use daily % change
daily_ret = df['close'].pct_change()#drop the 1st value - nan
daily_ret.dropna(inplace=True)#daily %change
daily_ret.head()Altyapı:
date
1986-01-030.0061551986-01-06-0.0010911986-01-070.0149541986-01-08-0.0272681986-01-09-0.008944Name: close,dtype: float64
Altyapı:
Sıradaki adım, bu günlük yüzde değişim değerlerini 'aylık yıllıklandırılmış oynaklığa' dönüştürmek. Aşağıdaki kodun ilk satırı, bu dönüşümü tek satırda yapabildiğimizi gösteriyor. İşte pandas'ın gücü! Ancak gelin her adımı sırasıyla açalım.
1. mnthly_annu'yu elde etmek için önce günlük getirilerimizde 'resample' yöntemini kullanıyoruz. Resample, veri dönemlerinin frekansını değiştirmemizi sağlar. Bir 'frekans ofset dizesi' alır; basitçe, istenen yeni frekansa karşılık gelen bir harf. Şunları içerir:
B iş günü frekansı
C özel iş günü frekansı
D takvim günü frekansı
W haftalık frekans
M ay sonu frekansı
Günlük getirilerden aylığa yeniden örneklemek istediğimiz için parametre olarak 'M' geçiriyoruz.
2. İkinci olarak, bu yeni aylık değere nasıl ulaşacağımıza karar vermeliyiz; örneğin, günlük değerleri toplayabilir, çarpabiliriz vb. Analizimiz için oynaklığın bir ölçüsünü istiyoruz ve standart sapma buna uygun, bu nedenle aylık oynaklığı elde etmek için yeniden örneklenen verilerimize std() ekleyeceğiz.
3. Son adım bu değeri yıllıklandırmak. Bunu, yıldaki dönem sayısı (12 ay) olduğu için 12'nin karekökü ile çarparak yapıyoruz. Bu da bize gereken yıllıklandırılmış aylık oynaklık değerlerini verir.
Veri analizinde en iyi uygulama, analiz boyunca veriyi görselleştirmektir. O halde yıllıklandırılmış oynaklık verimize bir göz atalım.
Aşağıdaki grafik, Kara Pazartesi ve 2008 Finansal Krizi gibi büyük piyasa olaylarını açıkça gösteriyor. matplotlib'in 'axvspan' yöntemi dikey çizgiler eklememize olanak tanır (yatay çizgiler için karşılığı axhspan'dır); oluşturulan dikdörtgenin genişliğini belirleyen 'xmin' ve 'xmax' parametrelerini alır; x eksenimiz datetime indeks olduğundan, vurgulamak istediğimiz yılları doğrudan geçebiliriz. Alfa parametresi, blok renginin saydamlığını ayarlayarak alttaki grafiği görmemizi sağlar.
mpatches niteliği özel bir açıklama kutusu oluşturmamıza olanak tanır. Bunu, önce renk, alfa ve istenen metni içeren 'labs' değişkenini tanımlayarak yaparız; ardından açıklamayı çizdirmek için plt.legend içindeki 'handles' parametresine bunu geçiririz.
#use pandas to resample returns per month and take Standard Dev as measure of Volatility#then annualize by multiplying by sqrt of number of periods (12)
mnthly_annu = daily_ret.resample('M').std()* np.sqrt(12)print(mnthly_annu.head())#we can see major market events show up in the volatility
plt.plot(mnthly_annu)
plt.axvspan('1987','1989',color='r',alpha=.5)
plt.axvspan('2008','2010',color='r',alpha=.5)
plt.title('Monthly Annualized vol - Black Monday and 2008 Financial Crisis highlighted')
labs = mpatches.Patch(color='red',alpha=.5, label="Black Monday & '08 Crash")
plt.legend(handles=[labs])Altyapı:
date
1986-01-310.0333171986-02-280.0235851986-03-310.0279611986-04-300.0374261986-05-310.027412Freq:M,Name: close,dtype: float64
<matplotlib.legend.Legend at 0x280b1ee6908>Altyapı:
Böylece pandas'taki güçlü yöntemlerden biri olan resample'ı gördük. Şimdi bir diğerini, 'groupby''u kullanalım. Aylık yıllıklandırılmış oynaklık değerlerimizden hedeflediğimiz AMVR metriğine ulaşmamız gerekiyor. Bunu yine sadece birkaç satır kodla başarabiliriz.
1. Önce, mnthly_annu değerlerimize 'groupby' yöntemini uygularız. Groupby, verilerin nasıl gruplanacağını belirten bir parametre gerektirir. Bu bir fonksiyon olabilir ya da bizim durumumuzda olduğu gibi bir Seri. 'mnthly_annu.index.year' geçiriyoruz; bu, mnthly_annu verilerinin datetime indeksindeki yıl özniteliğidir. Bu, veri setimizdeki 32 yılın her biri için aylık oynaklık değerlerini gruplayacaktır.
2. Sonra, veriyi artan düzende sıralayan 'rank' yöntemini uygularız.
3. Son olarak, aynı şeyi tekrar yapar ve her ay için tüm yıllar boyunca ortalamasını alırız. Bu bize nihai AMVR değerlerimizi verir!
#for each year rank each month based on volatility lowest=1 Highest=12
ranked = mnthly_annu.groupby(mnthly_annu.index.year).rank()#average the ranks over all years for each month
final = ranked.groupby(ranked.index.month).mean()
final.describe()Altyapı:
count 12.000000
mean 6.450521
std 0.627458
min 5.21875025%6.03101350%6.49100475%6.704545
max 7.531250Name: close,dtype: float64
Altyapı:
Bu bize nihai Ortalama Aylık Oynaklık Sıralamalarını verir. Sayısal olarak 10. ayın (Ekim) en yüksek, 12'nin (Aralık) en düşük olduğunu görebiliyoruz.
#the final average results over 32 years
final
Altyapı:
date
16.81818226.66666736.57575847.30303056.60606166.03030376.03125085.87500096.406250107.531250116.343750125.218750Name: close,dtype: float64
Altyapı:
Verimiz için doğru görselleştirmeyi seçmek önemlidir; çünkü verinin hikayesini en etkili grafik ve çizimlerle anlatmak istiyoruz. Analizimizde, en yüksek ve en düşük oynaklığı net biçimde göstermek istiyoruz. Bunun için matplotlib'in çubuk grafiğini kullanacak, en yüksek etki için uygun şekilde açıklama ve renklendirme yapacağız.
1. İstenen çubuğu b_plot (b_plot[9]) ile indeksleyerek, en yüksek ve en düşük değerleri vurgulamak için rengini ayarlayabiliriz.
2. AMVR değerlerini ilgili çubuklarına eklemek için, her bir AMVR değeri ('final' değişkeni) üzerinde enumerate eder ve sonucu 2 ondalığa yuvarlarız. 'i' ve 'v', 'final' değişkeninin 'index' ve 'value' ögelerini temsil eder; dolayısıyla 'i' 1-12 arasında değişirken, 'value' 2 ondalığa yuvarlanmış AMVR değerini verir. Bu değişkenleri plt.text() içinde kullanırız. plt.txt()'nin ilk parametresi etiketin x ekseni konumudur; bu nedenle her çubuk için 'i' geçirir ve 0,8 ile ortalarız. İkinci parametre 'final' değerinin dizge halidir.
3. Ortalama değeri göstermek için axhline kullanır ve sağ üst köşede bir açıklama eklemek üzere 'label' değerini kullanırız. Basitçe y ekseni değerini (ortalama) geçin, ardından stil verin.
Bu, incelediğimiz olgunun şimdiye kadarki en iyi görselini verir. Ekim'in en oynak ay, Aralık'ın ise en az oynak ay olduğunu net biçimde görebiliyoruz. Önemli olarak, mutlak anlamda Aralık'ın daha 'aşırı' değer olduğunu da görüyoruz. Bu, bir sonraki bölümdeki analizimiz için önemli olacak.
#plot results for ranked s&p 500 volatility#clearly October has the highest AMVR#and December has the lowest#mean of 6.45 is plotted
b_plot = plt.bar(x=final.index,height=final)
b_plot[9].set_color('g')
b_plot[11].set_color('r')for i,v inenumerate(round(final,2)):
plt.text(i+.8,1,str(v), color='black', fontweight='bold')
plt.axhline(final.mean(),ls='--',color='k',label=round(final.mean(),2))
plt.title('Average Monthly Volatility Ranking S&P500 since 1986')
plt.legend()
plt.show()Altyapı:
Verimiz bu şekilde; şimdi Hipotez testine geçelim…
Hipotez Testi: Soru Nedir?
Hipotez testi, veri biliminin en temel tekniklerinden biridir; ancak en göz korkutucu ve yanlış anlaşılanlardan da biridir. Bunun nedeni, İstatistik 101'de şu şekilde öğretilmesidir:
"t-testi yapın; tek taraflı mı çift taraflı mı? Uygun bir test istatistiği seçin, örneğin Welch'in t-testi; serbestlik derecelerini hesaplayın; t skorunu hesaplayın; tablodan kritik değeri bulun; kritik değeri t istatistiği ile karşılaştırın ……"
Anlaşılır şekilde, bu, hangi testin yapılacağı ve nasıl yapılacağı konusunda kafa karışıklığına yol açar. Oysa, hipotez testi için bu klasik istatistiksel tekniklerin tümü, çok az hesaplama gücümüz olduğu bir dönemde geliştirildi ve sadece p-değeri hesaplamak için kapalı form analitik çözümlerdi, hepsi bu! Üstelik kısıtlayıcı ve kimi zaman opak varsayımları nedeniyle verilen duruma uygun doğru formülü seçme zorunluluğunun getirdiği karmaşıklıkla birlikte.
Fakat sevinin!
Daha iyi bir yol var. Simülasyon.
Simülasyonun bize nasıl yardımcı olabileceğini anlamak için, hipotez testinin ne olduğunu hatırlayalım:
Şunu test etmek isteriz: "verimizde gözlenen etkinin gerçek olup olmadığı veya yalnızca rastlantı sonucu meydana gelip gelmediği" ve bu testi yapmak için şunları yaparız:
Uygun bir 'test istatistiği' seçin: bu, gözlenen etkiyi ölçen basit bir sayıdır. Bizim durumumuzda AMVR'nin ortalamadan mutlak sapmasını seçeceğiz.
Sıfır Hipotezi kurun: bu, gözlenen etkinin bulunmadığı veri sürümüdür. Bizim durumumuzda, verinin etiketlerini defalarca karıştıracağız (permütasyon). Gerekçesi aşağıda detaylandırılmıştır.
Bir p-değeri hesaplayın: bu, gözlenen etkinin sıfır veri kümesinde görülme olasılığıdır; başka bir deyişle, şans eseri. Bunu sıfır verisinin tekrarlı simülasyonu yoluyla yaparız. Bizim durumumuzda, verinin 'date' etiketlerini birçok kez karıştırır ve çoklu simülasyonlarda test istatistiğimizin ortaya çıkışını sayarız.
Hipotez testi 3 adımda bu kadar! Hangi olguyu test edersek edelim, soru her zaman aynıdır: "gözlenen etki gerçek mi, yoksa şans eseri mi"
Simülasyonun gerçek gücü, model varsayımlarımızı kod aracılığıyla açıkça ifade etmemizi gerektirmesidir. Oysa klasik teknikler, varsayımları konusunda bir 'kara kutu' olabilir.
Aşağıdaki örnek: Sol grafik, belirli bir olasılıkla (yeşil) gözlenen etkiyle gerçek veriyi gösterir. Sağ grafik, şans eseri gözlenen etkinin ne zaman görüldüğünü kaydeden simüle edilmiş sıfır verimizi (kırmızı) gösterir. Bu, hipotez testinin temelidir: sıfır verimiz üzerinde gözlenen etkiyi görme olasılığı nedir.
Hipotez testinin en kritik kısmı, cevaplamaya çalıştığımız sorunun net olmasıdır. Bizim durumumuzda şu soruyu soruyoruz:
"En uç değer şans eseri meydana gelebilir mi?"
En uç değeri, ortalamadan en büyük mutlak AMVR sapması olarak tanımlıyoruz. Bu soru, sıfır hipotezimizi oluşturur.
Verimizde en uç değer, ortalamadan en büyük mutlak sapmayı aradığımız için, yalnızca en yüksek oynaklığı değil, Aralık (1,23) değeridir; Ekim (1,08) değil.
1. Mutlak sapmayı elde etmek için, her bir değerden ortalamayı çıkarır ve abs() fonksiyonunu uygularız.
2. sort_values() yöntemini kullanarak sonuçları küçükten büyüğe sıralayabiliriz. Ardından en büyük iki değeri, Ekim ve Aralık'ı seçeriz.
#take abs value move from the mean#we see Dec and Oct are the biggest abs moves
fin =abs(final - final.mean())print(fin.sort_values())
Oct_value = fin[10]
Dec_value = fin[12]print('Extreme Dec value:', Dec_value)print('Extreme Oct value:', Oct_value)Altyapı:
date
90.044271110.10677130.12523750.15554020.21614610.36766170.41927160.42021880.57552140.852509101.080729121.231771Name: close,dtype: float64
Extreme Dec value:1.231770833333333
Extreme Oct value:1.080729166666667Altyapı:
Simülasyon
Artık hangi soruyu sorduğumuzu bildiğimize göre, 'Sıfır Modelimizi' kurmamız gerekiyor.
Burada birkaç seçenek var:
Parametrik modeller. Veri dağılımı hakkında iyi bir fikrimiz olsaydı ya da varsayım yapsaydık, 'klasik' hipotez test tekniklerini, t-testi, X², tek yönlü ANOVA vb. kullanabilirdik. Bu modeller kısıtlayıcı olabilir ve araştırmacı varsayımlarını tam olarak anlamıyorsa biraz kara kutu gibidir.
Doğrudan Simülasyon. veri üretim süreci hakkında varsayımlar yapıp doğrudan simüle edebiliriz. Örneğin, modellediğimiz finansal veri için bir ARMA zaman serisi modeli belirleyip mevsimsellik içermeyecek şekilde kasten tasarlayabiliriz. Bu, problemimiz için makul bir seçenek olabilir. Ancak S&P500'ün veri üretim sürecini biliyor olsaydık, zaten zengin olmuştuk!
Yeniden Örnekleme yoluyla Simülasyon. Biz bu yaklaşımı kullanacağız. Mevcut veri setinden rastgele defalarca örnek alıp etiketleri karıştırarak, gözlenen etkiyi verimizdeki tüm etiketler arasında (bizim durumumuzda etiketler tarihlerdir) eşit olasılıklı hale getirebiliriz; böylece istenen sıfır veri setini elde ederiz.
Örnekleme geniş bir konu, ancak biz belirli bir tekniğe odaklanacağız: permütasyon veya karıştırma.
İstenen sıfır modeli elde etmek için, içinde mevsimsellik olmayan bir veri seti oluşturmamız gerekir. Sıfır doğruysa, veride mevsimsellik yoktur ve gözlenen etki sadece şans eseridir, o zaman her ayın (Oca, Şub, vb.) etiketleri anlamsızdır; dolayısıyla veriyi defalarca karıştırarak, klasik istatistiklerin 'sıfır hipotezi altında test istatistiğinin örnekleme dağılımı' diyeceği şeyi oluşturabiliriz. Bu, gözlenen olguyu (aşırı Aralık değeri) tüm aylar için eşit olasılıklı hale getirme etkisine sahiptir; bu da tam olarak sıfır modelimizin gerektirdiğidir.
Modern işlem gücünün simülasyon tekniklerini ne kadar güçlü kıldığını göstermek için, bu örnekteki kod aslında günlük fiyat verilerini permüte eder; bu çok daha fazla işlem gücü gerektirir; yine de modern bir CPU'da saniyeler içinde tamamlanır.
Not: Bizim durumumuzda, günlük veya aylık etiketleri karıştırmak istenen sıfır veri setini verir.
Shuffle 'date' label to create null dataset
Altyapı:
Örnekleme öğrenmek için harika bir kaynak: Julian Simon.
Not: Testimizin kurulumu, 'klasik' yöntemler (Welch'in t-testi veya ANOVA vb.) kullanılarak yapılan iki taraflı bir teste denktir; çünkü ortalamanın üzerinde ya da altında en uç değeri ilgileniyoruz.
Bu kararlar tasarım tercihidir ve bu özgürlüğe sahibiz; çünkü Sıfır Modeli sadece bir Modeldir! Bu, parametrelerini istediğimiz gibi belirleyebileceğimiz anlamına gelir; önemli olan, cevaplamaya çalıştığımız soruyu gerçekten netleştirmektir.
Dolayısıyla Python kullanarak çok sayıda veri simüle edip bir sıfır veri seti oluşturmak istiyoruz. Bunu başarmak için, her seferinde 'date' etiketlerini permüte ederek 12 AMVR'den oluşan 1000 set simüle edeceğiz; böylece örnekleme dağılımını oluşturacağız. Bu kodun çıktısı, p-hacking bölümünde yer almaktadır.
1. Önce, sonuçlarımızı saklamak için birkaç kap tanımlarız: bir pd.Dataframe() ve basit bir dizi [ ] kullanacağız.
2. Ardından bir sayaç tanımlar, sıfıra ayarlarız ve simüle veriyi oluşturmak için 1000 yinelemeli bir for döngüsüne başlarız.
3. For döngüsünün ilk satırı, orijinal günlük getirileri (dersin başından) alır ve pandas sample() yöntemini kullanır. Bu, günlük getiri verilerinden belirli sayıda rastgele örnekler. Bizim durumumuzda 8191 veri noktamız var (32 yılda 252 işlem günü). Ayrıca reset_index() ile indeksi düşeriz, böylece yeni bir indeks ekleyebiliriz. Bunu, orijinal tarih indeksinin verilerle birlikte karıştığı, oysa bizim yalnızca veriyi 'karıştırmak' istediğimiz için yapmamız gerekir.
4. Bir sonraki satır, günlük_ret_shuffle indeksini orijinal veriyle aynı uzunlukta bir pd.bdate_range ile yeniden atayarak yeni tarih indeksini ekler. 7 gün yerine (Pzt - Cuma) 5 iş günü istediğimiz için date_range yerine bdate_range kullanırız.
5. 'Karıştırılmış' veriyi ürettiğimize göre, AMVR değerlerimizi oluşturmak için başta yaptığımızla aynı veri düzenlemeyi yaparız. Sonraki 3 kod satırı bunu yapıyor.
6. Simüle AMVR değerlerimizi elde ettiğimize göre, bunları simülasyonun tüm 1000 çalıştırmasını saklayacak veri çerçevemize eklememiz gerekiyor. pd.concat mevcut veri çerçevemizi alır ve sonraki her veri çerçevesini sonuna ekler. Axis 1'i seçiyoruz, böylece sütunlar halinde veri elde ediyoruz.
7. Maximonth, her simülasyondan sadece en yüksek değeri saklayan bir değişkendir (bunu daha sonra analizimizde kullanacağız).
8. Analizimiz mutlak AMVR değerleri gerektirdiğinden, sonraki 3 kod satırı 1000 AMVR çalışmasının tümünü tek bir büyük diziye düzleştirir. Ardından ortalamasını alır ve bu ortalamadan bireysel değerleri çıkarır, son olarak mutlak değeri alırız.
9. 'Sadece en yüksek' veri için de aynı şeyi yaparız. Not: burada bir veri çerçevesi yerine liste kullandık; bu da her bir en yüksek değerimiz için abs(AMVR) hesaplamak üzere bir liste üreteci kullanabileceğimiz anlamına gelir.
Artık analizimizi tamamlamak için ihtiyaç duyduğumuz tüm verilere sahibiz: hem orijinal gözlemler hem de yeni simüle veri setimiz.
#as our Null is that no seasonality exists or alternatively that the month does not matter in terms of AMVR,#we can shuffle 'date' labels#for simplicity, we will shuffle the 'daily' return data, which has the same effect as shuffling 'month' labels#generate null data
new_df_sim = pd.DataFrame()
highest_only =[]
count=0
n=1000for i inrange(n):#sample same size as dataset, drop timestamp
daily_ret_shuffle = daily_ret.sample(8191).reset_index(drop=True)#add new timestamp to shuffled data
daily_ret_shuffle.index =(pd.bdate_range(start='1986-1-3',periods=8191))#then follow same data wrangling as before...
mnthly_annu = daily_ret_shuffle.resample('M').std()* np.sqrt(12)
ranked = mnthly_annu.groupby(mnthly_annu.index.year).rank()
sim_final = ranked.groupby(ranked.index.month).mean()#add each of 1000 sims into df
new_df_sim = pd.concat([new_df_sim,sim_final],axis=1)#also record just highest AMVR for each year (we will use this later for p-hacking explanation)
maxi_month =max(sim_final)
highest_only.append(maxi_month)#calculate absolute deviation in AMVR from the mean
all_months = new_df_sim.values.flatten()
mu_all_months = all_months.mean()
abs_all_months =abs(all_months-mu_all_months)#calculate absolute deviation in highest only AMVR from the mean
mu_highest = np.mean(highest_only)
abs_highest =[abs(x - mu_all_months)for x in highest_only]Altyapı:
p-Hacking
İşte ilginç kısım. Test etmek için bir hipotez kurduk, verinin 'date' etiketlerini karıştırarak simüle veri ürettik; şimdi sıfır hipotezinin (mevsimsellik yok) doğru olduğu varsayımı altında, Aralık sonucumuz kadar anlamlı bir sonucu gözlemleme olasılığını bulmak için hipotez testimizi yapmamız gerekiyor.
Teste başlamadan önce beklentilerimizi belirleyelim.
%5 anlamlılık düzeyi verildiğinde, en az bir anlamlı sonuç görme olasılığı nedir?
= 1-p(anlamsız)
= 1-(1–0.05)¹²
= 0.46
yani sıfırımız doğruyken en az bir ayda anlamlı sonuç görme olasılığı %46.
Şimdi soralım: her bir bireysel test için (12 ayın her birinin mutlak AMVR'sini ortalama ile karşılaştırmak) mevsimsellik içermeyen rastgele verimizde kaç tane anlamlı değer görmeyi beklemeliyiz?
12 x 0.05 = 0.6
Dolayısıyla, 0,05 anlamlılık düzeyinde, beklenen yalancı pozitif oranı 0,6 olmalıdır. Başka bir deyişle, her testte (sıfır verisiyle) tüm 12 ayın AMVR'sini ortalama ile karşılaştırırken, 0,6 ayda anlamlı sonuç görürüz. (elbette bir aydan azı sonuç gösteremez, ancak tekrarlı testlerde matematik bu sayıya yaklaşacaktır).
Bu çalışmanın başından beri, cevaplamaya çalıştığımız soruyu netleştirmenin önemini vurguladık. Az önce hesapladığımız beklentilerdeki sorun, 12 ayın tamamına karşı anlamlı bir sonuç test ettiğimizi varsaymış olmamız. Bu yüzden en az bir yalancı pozitif görme olasılığı %46 gibi yüksek.
Bu, arama alanımızı genişletip anlamlı bir sonuç bulma olasılığını artırdığımız çoklu karşılaştırma yanlılığına bir örnektir. Bu bir sorun; çünkü bu olguyu, modelimizin 'istenen' p-değerini veren parametrelerini seçmek için kötüye kullanabiliriz.
p-Hacking'in özü budur
p-hacking etkisini ve çokluluğun nasıl azaltılacağını göstermek için, aşağıdaki 2 soru arasındaki ince ama önemli farkı anlamamız gerekir:
"Aralık'ın bu kadar aşırı görünmesinin şans eseri olma olasılığı nedir?"
"Herhangi bir ayın bu kadar aşırı görünmesinin şans eseri olma olasılığı nedir?"
Simülasyonun güzelliği sadeliğindedir. Aşağıdaki kod, ilk sorunun p-değerini hesaplamak için ihtiyacımız olan her şeydir. Basitçe, verisetimizdeki tüm 12000 AMVR sapması (12 ay x 1000 deneme) arasında gözlenen Aralık değerinden büyük kaç değer olduğunu sayarız. %4,4 p-değeri elde ederiz; bir parça keyfi olan %5 eşiğine yakın, ama yine de anlamlı.
#count number of months in sim data where ave-vol-rank is >= Dec#Note: we are using Dec not Oct, as Dec has highest absolute deviation from the mean
count=0for i in abs_all_months:if i> Dec_value:
count+=1
ans = count/len(abs_all_months)print('p-value:', ans )Altyapı:
İkinci soruyu yanıtlamak ve çokluluğu önlemek için, 12000 AMVR sapmasının tamamıyla oluşturulan dağılımla kıyaslamak yerine, yalnızca her bir 1000 denemenin mutlak AMVR'leri arasından en yüksek değeri dikkate alırız. Bu, %23 p-değeri verir; kesinlikle anlamlı değil!
#same again but just considering highest AMVR for each of 100 trials
count=0for i in abs_highest:if i> Dec_value:
count+=1
ans = count/len(abs_highest)print('p-value:', ans )Altyapı:
Artık nihai sonuçlarımıza sahip olduğumuza göre, bu dağılımları çizerek p-Hacking etkisini ve analizimizin sonuçlarını görsel olarak gösterelim:
1. Önce, %5 anlamlılık düzeyini bulmak için np.quantile() kullanırız; bunu alt grafiklerde axvline ile göstereceğiz.
2. Ardından 4 kutulu alt grafiğimizi tanımlarız. Burada plt.subplots bize 'fig' = figürü ve 'ax1,ax2,ax3,ax4' = 4 alt grafiği temsil eden değişkenleri döndürür.
3. #grafik 1 birinci sütunu gösterir. Burada 'abs_all_months' verimizin histogramını tanımlar ve type='bar' seçeriz. Alt grafiklerde cumulative='True' ayarlarız; bu bize pdf yerine cdf verir. Bins, her bir değeri doldurmak için kaç çubuk olacağını tanımlar; verimiz için 30 makuldür. Sonra, anlamlılık ve gözlem değerlerini çizmek için axvline gibi daha önce öğrendiğimiz tekniklerle grafikleri biçimlendiririz.
<matplotlib.legend.Legend at 0x280b4eb0b00>Altyapı:
Sol sütun 1. soruyu, sağ sütun 2. soruyu yanıtlayan veridir. Üst sıra olasılık dağılımları, alt sıra ise Kümülatif Dağılım Fonksiyonu (CDF). Kırmızı kesikli çizgi, keyfi olarak belirlediğimiz %5 anlamlılık düzeyidir. Mavi çizgi, orijinal 1,23'lük aşırı Aralık AMVR değeridir.
Sol taraftaki grafik, orijinal Aralık değerinin %5 düzeyinde anlamlı olduğunu gösteriyor, ama ucu ucuna! Ancak çoklu karşılaştırma yanlılığını hesaba kattığımızda, sağdaki grafikte anlamlılık eşiği yaklaşık 1,2'den (mutlak AMVR) yaklaşık 1,6'ya yükseliyor (kırmızı çizgiye bakın).
Çoklu karşılaştırma yanlılığını hesaba kattığımızda, 1,23'lük Aralık değerimiz artık anlamlı değil!
Cevaplamaya çalıştığımız spesifik soruyu dikkate alarak ve çoklu karşılaştırma yanlılığından kaçınarak, modelimizi p-hacking yapmaktan ve olmayan bir anlamlı sonucu göstermekten kaçındık.
p-hacking'i daha fazla keşfetmek ve verilerimiz hakkında belirli bir hikaye anlatmak için nasıl kötüye kullanılabileceğini görmek için, FiveThirtyEight'in bu harika etkileşimli uygulamasına bakın
Sonuçlar
Hipotez testinin düşündüğümüz kadar korkutucu bir canavar olmadığını öğrendik. Her tür veri veya test istatistiği için modelinizi kurmak üzere yukarıdaki 3 adımı izleyin.
Doğru soruyu sormanın bilimsel analiz için hayati olduğunu gösterdik. İfade tarzındaki küçük bir değişiklik, çok farklı sonuçlara sahip çok farklı bir modele yol açabilir.
Umarız, daha ileri Python işlevselliğinin gücü ve istatistiksel test yapma yetkinliği artık aşikardır! Yalnızca birkaç satır kodla gerçek bir olguyu kurduk ve test ettik ve eyleme geçirilebilir sonuçlara vardık.
Çoklu karşılaştırma yanlılığını tanımanın ve düzeltmenin, p-hacking tuzaklarından kaçınmanın önemini tartıştık ve görünüşte anlamlı bir sonucun nasıl anlamsız hale gelebileceğini gösterdik.
Giderek artan 'büyük veri' ile birlikte, 'yeni' bulgular içeren bir makale üretme akademik baskısı ya da bir sonucu 'anlamlı' gösterme politik baskısı, p-hacking cazibesini artırıyor. Ne zaman buna saptığımızı fark edip uygun şekilde düzelterek daha iyi araştırmacılar olabilir ve nihayetinde daha doğru ve dolayısıyla eyleme geçirilebilir bilimsel sonuçlar üretebiliriz!
Yazar Notları: Sonuçlarımız, kısmen farklı bir veri setine sahip olmamızdan (32 yıl vs. 87 yıl) ve onların ilgilendiği ayın Ekim, bizde ise Aralık olmasından ötürü, orijinal Winton araştırmasından biraz farklılık gösteriyor. Ayrıca, onlar 'simüle veri' için açıklanmamış bir yöntem kullanırken, biz bu veriyi oluşturma yöntemimizi kod aracılığıyla açıkça ortaya koyduk. Bu çalışma boyunca belirli modelleme varsayımları yaptık; bunlar yine açıkça ifade edilmiştir ve kodda görülebilir. Bu tasarım ve modelleme tercihleri, açık oldukları sürece, bilimsel sürecin bir parçasıdır ve analiz değer taşır.
Algoritmalar, sistem tasarımı ve davranışsal senaryoları kapsayan bu temel sorularla teknik mülakat sürecine hakim olun. Uzman cevapları, kod örnekleri ve kanıtlanmış hazırlık stratejileri edinin.
Git deponuzu temiz tutmak için .gitignore’u nasıl kullanacağınızı öğrenin. Bu eğitim; temelleri, yaygın kullanım durumlarını ve başlamanıza yardımcı olacak pratik örnekleri kapsar!
"Son 32 yılda, ortalama olarak S&P500 için en oynak ay Ekim, en az oynak ay ise Aralık olmuştur".
Bu derste, Python kullanarak bu olgunun baştan sona analiz ve testini yaparak istatistiksel olarak anlamlı olup olmadığını belirleyeceğiz.
Veriyi canlandıracak bir forma dönüştürmek için Resample gibi pandas zaman serisi işlevlerini kullanarak ham hisse fiyatı verilerini düzenleyeceğiz.
Ardından, hantal formüller yerine Python ile simülasyon kullanarak hipotez testinin nasıl yapılacağını keşfedeceğiz.
Son olarak, istatistiksel analizde büyük bir sorun olan Çoklu Karşılaştırma Yanlılığı'nı ele alacak, bununla nasıl başa çıkılacağını öğrenecek ve matplotlib ile görsel olarak 'p-Hacking' etkisini göstereceğiz.
Hedefimiz:
Verimiz:
Bu analiz için Günlük S&P500 verisini kullanacağız; özellikle, 1986'dan 2018'e kadar olan ham günlük kapanış fiyatlarını kullanacağız (şaşırtıcı şekilde bulması zor olduğundan, herkese açık hale getirdim).
Bu yazının ilhamı, burada 87 yıl yerine 32 yıllık veriyle yeniden üreteceğimiz Winton çalışmasından geldi.
Pandas ile Düzenleme
Bazı aylarda görülen aşırı oynaklığın gerçekten anlamlı olup olmadığını ve dolayısıyla devam etme olasılığını yanıtlamak için, 32 yıllık fiyat verimizi incelediğimiz olguyu ortaya koyan bir formata dönüştürmemiz gerekiyor.
Seçtiğimiz format ortalama aylık oynaklık sıralaması (AMVR) olacak.
Aşağıdaki kod, ham fiyat verimizi bu formata nasıl getirdiğimizi gösteriyor. Hadi başlayalım!
Önce standart içe aktarmalar. (matplotlib.patches, histogramdaki tek tek çubukların stilini kontrol etmemizi sağlar)
Jupyter not defterlerinde grafikleri tam genişlikte göstermek için güzel bir numara:
Bu opsiyonel — denemekten çekinmeyin, ne olacağını görün!
Verimizi, yol parametresi alan read_csv yöntemiyle içe aktaralım; bizim durumumuzda veriye giden bir url. Ayrıca, indekste 'date'i kullanmasını ve verilen metinden tarihleri elinden geldiğince otomatik ayrıştırmasını söylüyoruz.
Bu bize S&P500'ün (SPX) ham, düzeltilmemiş kapanış fiyatlarını verir. Şimdi ham fiyatlarımızı günlük yüzde getirilerine dönüştürmemiz gerekiyor. Bunu yapmak için iki seçeneğimiz var:
Amacımız için, bu veri seti boyutunda (8000'in üzerinde değer) pandas hesabı anında yapabildiğinden yöntem 2'yi kullanacağız. Ayrıca, bir önceki güne göre fiyat değişimi olmadığı için 'NaN' olan ilk değeri düşerek verileri temizleyeceğiz. pct_change() opsiyonel 'periods' parametresi alır ve dönem kaymasını değiştirir. Bunu varsayılan birde bırakacağız.
Sıradaki adım, bu günlük yüzde değişim değerlerini 'aylık yıllıklandırılmış oynaklığa' dönüştürmek. Aşağıdaki kodun ilk satırı, bu dönüşümü tek satırda yapabildiğimizi gösteriyor. İşte pandas'ın gücü! Ancak gelin her adımı sırasıyla açalım.
1. mnthly_annu'yu elde etmek için önce günlük getirilerimizde 'resample' yöntemini kullanıyoruz. Resample, veri dönemlerinin frekansını değiştirmemizi sağlar. Bir 'frekans ofset dizesi' alır; basitçe, istenen yeni frekansa karşılık gelen bir harf. Şunları içerir:
Günlük getirilerden aylığa yeniden örneklemek istediğimiz için parametre olarak 'M' geçiriyoruz.
2. İkinci olarak, bu yeni aylık değere nasıl ulaşacağımıza karar vermeliyiz; örneğin, günlük değerleri toplayabilir, çarpabiliriz vb. Analizimiz için oynaklığın bir ölçüsünü istiyoruz ve standart sapma buna uygun, bu nedenle aylık oynaklığı elde etmek için yeniden örneklenen verilerimize std() ekleyeceğiz.
3. Son adım bu değeri yıllıklandırmak. Bunu, yıldaki dönem sayısı (12 ay) olduğu için 12'nin karekökü ile çarparak yapıyoruz. Bu da bize gereken yıllıklandırılmış aylık oynaklık değerlerini verir.
Veri analizinde en iyi uygulama, analiz boyunca veriyi görselleştirmektir. O halde yıllıklandırılmış oynaklık verimize bir göz atalım.
Aşağıdaki grafik, Kara Pazartesi ve 2008 Finansal Krizi gibi büyük piyasa olaylarını açıkça gösteriyor. matplotlib'in 'axvspan' yöntemi dikey çizgiler eklememize olanak tanır (yatay çizgiler için karşılığı axhspan'dır); oluşturulan dikdörtgenin genişliğini belirleyen 'xmin' ve 'xmax' parametrelerini alır; x eksenimiz datetime indeks olduğundan, vurgulamak istediğimiz yılları doğrudan geçebiliriz. Alfa parametresi, blok renginin saydamlığını ayarlayarak alttaki grafiği görmemizi sağlar.
mpatches niteliği özel bir açıklama kutusu oluşturmamıza olanak tanır. Bunu, önce renk, alfa ve istenen metni içeren 'labs' değişkenini tanımlayarak yaparız; ardından açıklamayı çizdirmek için plt.legend içindeki 'handles' parametresine bunu geçiririz.
Böylece pandas'taki güçlü yöntemlerden biri olan resample'ı gördük. Şimdi bir diğerini, 'groupby''u kullanalım. Aylık yıllıklandırılmış oynaklık değerlerimizden hedeflediğimiz AMVR metriğine ulaşmamız gerekiyor. Bunu yine sadece birkaç satır kodla başarabiliriz.
1. Önce, mnthly_annu değerlerimize 'groupby' yöntemini uygularız. Groupby, verilerin nasıl gruplanacağını belirten bir parametre gerektirir. Bu bir fonksiyon olabilir ya da bizim durumumuzda olduğu gibi bir Seri. 'mnthly_annu.index.year' geçiriyoruz; bu, mnthly_annu verilerinin datetime indeksindeki yıl özniteliğidir. Bu, veri setimizdeki 32 yılın her biri için aylık oynaklık değerlerini gruplayacaktır.
2. Sonra, veriyi artan düzende sıralayan 'rank' yöntemini uygularız.
3. Son olarak, aynı şeyi tekrar yapar ve her ay için tüm yıllar boyunca ortalamasını alırız. Bu bize nihai AMVR değerlerimizi verir!
Bu bize nihai Ortalama Aylık Oynaklık Sıralamalarını verir. Sayısal olarak 10. ayın (Ekim) en yüksek, 12'nin (Aralık) en düşük olduğunu görebiliyoruz.
Verimiz için doğru görselleştirmeyi seçmek önemlidir; çünkü verinin hikayesini en etkili grafik ve çizimlerle anlatmak istiyoruz. Analizimizde, en yüksek ve en düşük oynaklığı net biçimde göstermek istiyoruz. Bunun için matplotlib'in çubuk grafiğini kullanacak, en yüksek etki için uygun şekilde açıklama ve renklendirme yapacağız.
1. İstenen çubuğu b_plot (b_plot[9]) ile indeksleyerek, en yüksek ve en düşük değerleri vurgulamak için rengini ayarlayabiliriz.
2. AMVR değerlerini ilgili çubuklarına eklemek için, her bir AMVR değeri ('final' değişkeni) üzerinde enumerate eder ve sonucu 2 ondalığa yuvarlarız. 'i' ve 'v', 'final' değişkeninin 'index' ve 'value' ögelerini temsil eder; dolayısıyla 'i' 1-12 arasında değişirken, 'value' 2 ondalığa yuvarlanmış AMVR değerini verir. Bu değişkenleri plt.text() içinde kullanırız. plt.txt()'nin ilk parametresi etiketin x ekseni konumudur; bu nedenle her çubuk için 'i' geçirir ve 0,8 ile ortalarız. İkinci parametre 'final' değerinin dizge halidir.
3. Ortalama değeri göstermek için axhline kullanır ve sağ üst köşede bir açıklama eklemek üzere 'label' değerini kullanırız. Basitçe y ekseni değerini (ortalama) geçin, ardından stil verin.
Bu, incelediğimiz olgunun şimdiye kadarki en iyi görselini verir. Ekim'in en oynak ay, Aralık'ın ise en az oynak ay olduğunu net biçimde görebiliyoruz. Önemli olarak, mutlak anlamda Aralık'ın daha 'aşırı' değer olduğunu da görüyoruz. Bu, bir sonraki bölümdeki analizimiz için önemli olacak.
Verimiz bu şekilde; şimdi Hipotez testine geçelim…
Hipotez Testi: Soru Nedir?
Hipotez testi, veri biliminin en temel tekniklerinden biridir; ancak en göz korkutucu ve yanlış anlaşılanlardan da biridir. Bunun nedeni, İstatistik 101'de şu şekilde öğretilmesidir:
"t-testi yapın; tek taraflı mı çift taraflı mı? Uygun bir test istatistiği seçin, örneğin Welch'in t-testi; serbestlik derecelerini hesaplayın; t skorunu hesaplayın; tablodan kritik değeri bulun; kritik değeri t istatistiği ile karşılaştırın ……"
Anlaşılır şekilde, bu, hangi testin yapılacağı ve nasıl yapılacağı konusunda kafa karışıklığına yol açar. Oysa, hipotez testi için bu klasik istatistiksel tekniklerin tümü, çok az hesaplama gücümüz olduğu bir dönemde geliştirildi ve sadece p-değeri hesaplamak için kapalı form analitik çözümlerdi, hepsi bu! Üstelik kısıtlayıcı ve kimi zaman opak varsayımları nedeniyle verilen duruma uygun doğru formülü seçme zorunluluğunun getirdiği karmaşıklıkla birlikte.
Fakat sevinin!
Daha iyi bir yol var. Simülasyon.
Simülasyonun bize nasıl yardımcı olabileceğini anlamak için, hipotez testinin ne olduğunu hatırlayalım:
Şunu test etmek isteriz: "verimizde gözlenen etkinin gerçek olup olmadığı veya yalnızca rastlantı sonucu meydana gelip gelmediği" ve bu testi yapmak için şunları yaparız:
Yalnızca bir test vardır! Allen Downey'nin bu harika blogunda hipotez testine dair daha fazla detay var
Simülasyonun gerçek gücü, model varsayımlarımızı kod aracılığıyla açıkça ifade etmemizi gerektirmesidir. Oysa klasik teknikler, varsayımları konusunda bir 'kara kutu' olabilir.
Aşağıdaki örnek: Sol grafik, belirli bir olasılıkla (yeşil) gözlenen etkiyle gerçek veriyi gösterir. Sağ grafik, şans eseri gözlenen etkinin ne zaman görüldüğünü kaydeden simüle edilmiş sıfır verimizi (kırmızı) gösterir. Bu, hipotez testinin temelidir: sıfır verimiz üzerinde gözlenen etkiyi görme olasılığı nedir.
Hipotez testinin en kritik kısmı, cevaplamaya çalıştığımız sorunun net olmasıdır. Bizim durumumuzda şu soruyu soruyoruz:
"En uç değer şans eseri meydana gelebilir mi?"
En uç değeri, ortalamadan en büyük mutlak AMVR sapması olarak tanımlıyoruz. Bu soru, sıfır hipotezimizi oluşturur.
Verimizde en uç değer, ortalamadan en büyük mutlak sapmayı aradığımız için, yalnızca en yüksek oynaklığı değil, Aralık (1,23) değeridir; Ekim (1,08) değil.
1. Mutlak sapmayı elde etmek için, her bir değerden ortalamayı çıkarır ve abs() fonksiyonunu uygularız.
2. sort_values() yöntemini kullanarak sonuçları küçükten büyüğe sıralayabiliriz. Ardından en büyük iki değeri, Ekim ve Aralık'ı seçeriz.
Simülasyon
Artık hangi soruyu sorduğumuzu bildiğimize göre, 'Sıfır Modelimizi' kurmamız gerekiyor.
Burada birkaç seçenek var:
Örnekleme geniş bir konu, ancak biz belirli bir tekniğe odaklanacağız: permütasyon veya karıştırma.
İstenen sıfır modeli elde etmek için, içinde mevsimsellik olmayan bir veri seti oluşturmamız gerekir. Sıfır doğruysa, veride mevsimsellik yoktur ve gözlenen etki sadece şans eseridir, o zaman her ayın (Oca, Şub, vb.) etiketleri anlamsızdır; dolayısıyla veriyi defalarca karıştırarak, klasik istatistiklerin 'sıfır hipotezi altında test istatistiğinin örnekleme dağılımı' diyeceği şeyi oluşturabiliriz. Bu, gözlenen olguyu (aşırı Aralık değeri) tüm aylar için eşit olasılıklı hale getirme etkisine sahiptir; bu da tam olarak sıfır modelimizin gerektirdiğidir.
Modern işlem gücünün simülasyon tekniklerini ne kadar güçlü kıldığını göstermek için, bu örnekteki kod aslında günlük fiyat verilerini permüte eder; bu çok daha fazla işlem gücü gerektirir; yine de modern bir CPU'da saniyeler içinde tamamlanır.
Not: Bizim durumumuzda, günlük veya aylık etiketleri karıştırmak istenen sıfır veri setini verir.
Örnekleme öğrenmek için harika bir kaynak: Julian Simon.
Not: Testimizin kurulumu, 'klasik' yöntemler (Welch'in t-testi veya ANOVA vb.) kullanılarak yapılan iki taraflı bir teste denktir; çünkü ortalamanın üzerinde ya da altında en uç değeri ilgileniyoruz.
Bu kararlar tasarım tercihidir ve bu özgürlüğe sahibiz; çünkü Sıfır Modeli sadece bir Modeldir! Bu, parametrelerini istediğimiz gibi belirleyebileceğimiz anlamına gelir; önemli olan, cevaplamaya çalıştığımız soruyu gerçekten netleştirmektir.
Dolayısıyla Python kullanarak çok sayıda veri simüle edip bir sıfır veri seti oluşturmak istiyoruz. Bunu başarmak için, her seferinde 'date' etiketlerini permüte ederek 12 AMVR'den oluşan 1000 set simüle edeceğiz; böylece örnekleme dağılımını oluşturacağız. Bu kodun çıktısı, p-hacking bölümünde yer almaktadır.
1. Önce, sonuçlarımızı saklamak için birkaç kap tanımlarız: bir pd.Dataframe() ve basit bir dizi [ ] kullanacağız.
2. Ardından bir sayaç tanımlar, sıfıra ayarlarız ve simüle veriyi oluşturmak için 1000 yinelemeli bir for döngüsüne başlarız.
3. For döngüsünün ilk satırı, orijinal günlük getirileri (dersin başından) alır ve pandas sample() yöntemini kullanır. Bu, günlük getiri verilerinden belirli sayıda rastgele örnekler. Bizim durumumuzda 8191 veri noktamız var (32 yılda 252 işlem günü). Ayrıca reset_index() ile indeksi düşeriz, böylece yeni bir indeks ekleyebiliriz. Bunu, orijinal tarih indeksinin verilerle birlikte karıştığı, oysa bizim yalnızca veriyi 'karıştırmak' istediğimiz için yapmamız gerekir.
4. Bir sonraki satır, günlük_ret_shuffle indeksini orijinal veriyle aynı uzunlukta bir pd.bdate_range ile yeniden atayarak yeni tarih indeksini ekler. 7 gün yerine (Pzt - Cuma) 5 iş günü istediğimiz için date_range yerine bdate_range kullanırız.
5. 'Karıştırılmış' veriyi ürettiğimize göre, AMVR değerlerimizi oluşturmak için başta yaptığımızla aynı veri düzenlemeyi yaparız. Sonraki 3 kod satırı bunu yapıyor.
6. Simüle AMVR değerlerimizi elde ettiğimize göre, bunları simülasyonun tüm 1000 çalıştırmasını saklayacak veri çerçevemize eklememiz gerekiyor. pd.concat mevcut veri çerçevemizi alır ve sonraki her veri çerçevesini sonuna ekler. Axis 1'i seçiyoruz, böylece sütunlar halinde veri elde ediyoruz.
7. Maximonth, her simülasyondan sadece en yüksek değeri saklayan bir değişkendir (bunu daha sonra analizimizde kullanacağız).
8. Analizimiz mutlak AMVR değerleri gerektirdiğinden, sonraki 3 kod satırı 1000 AMVR çalışmasının tümünü tek bir büyük diziye düzleştirir. Ardından ortalamasını alır ve bu ortalamadan bireysel değerleri çıkarır, son olarak mutlak değeri alırız.
9. 'Sadece en yüksek' veri için de aynı şeyi yaparız. Not: burada bir veri çerçevesi yerine liste kullandık; bu da her bir en yüksek değerimiz için abs(AMVR) hesaplamak üzere bir liste üreteci kullanabileceğimiz anlamına gelir.
Artık analizimizi tamamlamak için ihtiyaç duyduğumuz tüm verilere sahibiz: hem orijinal gözlemler hem de yeni simüle veri setimiz.
p-Hacking
İşte ilginç kısım. Test etmek için bir hipotez kurduk, verinin 'date' etiketlerini karıştırarak simüle veri ürettik; şimdi sıfır hipotezinin (mevsimsellik yok) doğru olduğu varsayımı altında, Aralık sonucumuz kadar anlamlı bir sonucu gözlemleme olasılığını bulmak için hipotez testimizi yapmamız gerekiyor.
Teste başlamadan önce beklentilerimizi belirleyelim.
%5 anlamlılık düzeyi verildiğinde, en az bir anlamlı sonuç görme olasılığı nedir?
= 1-p(anlamsız)
= 1-(1–0.05)¹²
= 0.46
yani sıfırımız doğruyken en az bir ayda anlamlı sonuç görme olasılığı %46.
Şimdi soralım: her bir bireysel test için (12 ayın her birinin mutlak AMVR'sini ortalama ile karşılaştırmak) mevsimsellik içermeyen rastgele verimizde kaç tane anlamlı değer görmeyi beklemeliyiz?
12 x 0.05 = 0.6
Dolayısıyla, 0,05 anlamlılık düzeyinde, beklenen yalancı pozitif oranı 0,6 olmalıdır. Başka bir deyişle, her testte (sıfır verisiyle) tüm 12 ayın AMVR'sini ortalama ile karşılaştırırken, 0,6 ayda anlamlı sonuç görürüz. (elbette bir aydan azı sonuç gösteremez, ancak tekrarlı testlerde matematik bu sayıya yaklaşacaktır).
Bu çalışmanın başından beri, cevaplamaya çalıştığımız soruyu netleştirmenin önemini vurguladık. Az önce hesapladığımız beklentilerdeki sorun, 12 ayın tamamına karşı anlamlı bir sonuç test ettiğimizi varsaymış olmamız. Bu yüzden en az bir yalancı pozitif görme olasılığı %46 gibi yüksek.
Bu, arama alanımızı genişletip anlamlı bir sonuç bulma olasılığını artırdığımız çoklu karşılaştırma yanlılığına bir örnektir. Bu bir sorun; çünkü bu olguyu, modelimizin 'istenen' p-değerini veren parametrelerini seçmek için kötüye kullanabiliriz.
p-hacking etkisini ve çokluluğun nasıl azaltılacağını göstermek için, aşağıdaki 2 soru arasındaki ince ama önemli farkı anlamamız gerekir:
Simülasyonun güzelliği sadeliğindedir. Aşağıdaki kod, ilk sorunun p-değerini hesaplamak için ihtiyacımız olan her şeydir. Basitçe, verisetimizdeki tüm 12000 AMVR sapması (12 ay x 1000 deneme) arasında gözlenen Aralık değerinden büyük kaç değer olduğunu sayarız. %4,4 p-değeri elde ederiz; bir parça keyfi olan %5 eşiğine yakın, ama yine de anlamlı.
İkinci soruyu yanıtlamak ve çokluluğu önlemek için, 12000 AMVR sapmasının tamamıyla oluşturulan dağılımla kıyaslamak yerine, yalnızca her bir 1000 denemenin mutlak AMVR'leri arasından en yüksek değeri dikkate alırız. Bu, %23 p-değeri verir; kesinlikle anlamlı değil!
Artık nihai sonuçlarımıza sahip olduğumuza göre, bu dağılımları çizerek p-Hacking etkisini ve analizimizin sonuçlarını görsel olarak gösterelim:
1. Önce, %5 anlamlılık düzeyini bulmak için np.quantile() kullanırız; bunu alt grafiklerde axvline ile göstereceğiz.
2. Ardından 4 kutulu alt grafiğimizi tanımlarız. Burada plt.subplots bize 'fig' = figürü ve 'ax1,ax2,ax3,ax4' = 4 alt grafiği temsil eden değişkenleri döndürür.
3. #grafik 1 birinci sütunu gösterir. Burada 'abs_all_months' verimizin histogramını tanımlar ve type='bar' seçeriz. Alt grafiklerde cumulative='True' ayarlarız; bu bize pdf yerine cdf verir. Bins, her bir değeri doldurmak için kaç çubuk olacağını tanımlar; verimiz için 30 makuldür. Sonra, anlamlılık ve gözlem değerlerini çizmek için axvline gibi daha önce öğrendiğimiz tekniklerle grafikleri biçimlendiririz.
Sol sütun 1. soruyu, sağ sütun 2. soruyu yanıtlayan veridir. Üst sıra olasılık dağılımları, alt sıra ise Kümülatif Dağılım Fonksiyonu (CDF). Kırmızı kesikli çizgi, keyfi olarak belirlediğimiz %5 anlamlılık düzeyidir. Mavi çizgi, orijinal 1,23'lük aşırı Aralık AMVR değeridir.
Sol taraftaki grafik, orijinal Aralık değerinin %5 düzeyinde anlamlı olduğunu gösteriyor, ama ucu ucuna! Ancak çoklu karşılaştırma yanlılığını hesaba kattığımızda, sağdaki grafikte anlamlılık eşiği yaklaşık 1,2'den (mutlak AMVR) yaklaşık 1,6'ya yükseliyor (kırmızı çizgiye bakın).
Cevaplamaya çalıştığımız spesifik soruyu dikkate alarak ve çoklu karşılaştırma yanlılığından kaçınarak, modelimizi p-hacking yapmaktan ve olmayan bir anlamlı sonucu göstermekten kaçındık.
p-hacking'i daha fazla keşfetmek ve verilerimiz hakkında belirli bir hikaye anlatmak için nasıl kötüye kullanılabileceğini görmek için, FiveThirtyEight'in bu harika etkileşimli uygulamasına bakın
Sonuçlar
Yazar Notları: Sonuçlarımız, kısmen farklı bir veri setine sahip olmamızdan (32 yıl vs. 87 yıl) ve onların ilgilendiği ayın Ekim, bizde ise Aralık olmasından ötürü, orijinal Winton araştırmasından biraz farklılık gösteriyor. Ayrıca, onlar 'simüle veri' için açıklanmamış bir yöntem kullanırken, biz bu veriyi oluşturma yöntemimizi kod aracılığıyla açıkça ortaya koyduk. Bu çalışma boyunca belirli modelleme varsayımları yaptık; bunlar yine açıkça ifade edilmiştir ve kodda görülebilir. Bu tasarım ve modelleme tercihleri, açık oldukları sürece, bilimsel sürecin bir parçasıdır ve analiz değer taşır.
Python ile finans hakkında daha fazla bilgi edinmek isterseniz, DataCamp'in Finans için Python'a Giriş kursunu alın ve Finans için Python Eğitimi: Algoritmik Alım Satım'a göz atın.
Daha fazlası için beni twitter.com/pdquant'ta takip edin