2021-05-31
Author: 98hyun
Published: 2021-05-31
Tags: 데이터분석 데이터베이스 개발환경 시각화 통계
20210430 post update
20210430 post에 분류표를 평가하기 위한 기준에 대해서 설명한 내용들을 코드와 함께 추가 했다. update 내용 보러가기
logloss
신용카드 사용자 연체 예측 AI 경진대회를 풀며 생각한 내용이다. logloss는 보통 multi classfication문제에 많이 등장한다.
만약 보기 5개가 있는 문제의 정답을 1로 '나 이거 알아'한 사람의 정답과 '헷갈리는데 이거'로 한 사람의 정답은 같다고 볼 수 있을까?
logloss는 모델이 예측한 확률 값을 직접적으로 반영하여 평가한다. 정확히 말하면 음의 log함수에 넣어서 확률값을 변환을 시킨 값이다. 즉, 잘못 예측할수록, 패널티를 부여한다.
code
## 아래 코드는 직접 사용한 코드를 가져왔다. 모든 코드를 보려면 https://www.kaggle.com/hwangchanghyun/dacon14 보면 된다. ## 주목할 점은 objective 와 eval_metric이다. ## objective는 '뭐 할꺼야?'의 답을 나타낸다. eval_metric은 '잘 되고있는지 봐줄게'다. ## logloss는 확률값을 알려준다고 했다. 즉, 1 2 3을 알아맞추는데 0.1 0.1 0.8이라고 알려준다. ## 분류표를 기억한다면 조금 낯설것이다. 정확히 0또는 1로 떨어지는 값이 아닌 어떤 값이 될 확률을 알려주니. 신용카드문제가 점수 채점을 logloss로 해서 그렇다. import xgboost as xgb params={ 'objective':'multi:softprob' ## multi:softmax도 있다. softmax로 하면 0,1 로 나뉘는것이다. ## 자연스럽게 eval_metric도 merror로 바뀐다. 'random_state':71, 'n_estimators':1000 } model=xgb.XGBClassifier(**params) model.fit(X_train,y_train, eval_set=[(X_train, y_train),(X_test,y_test)], eval_metric='mlogloss',verbose=True, early_stopping_rounds=10)
basic statistic
R을 사용하여 통계검정 방법들을 공부했다.
한번 알아두면 쉽게 할 수있는 방법들이다.
code
## 1. 기술통계 mystats=function(x,na.omit=F){ if(na.omit){ x=x[!is.na(x)] } m=mean(x) n=length(x) s=sd(x) skew=sum((x-m)^3/s^3)/n kurt=sum((x-m)^4/s^4)/n-3 return (c(n=n,mean=m,stdev=s,skew=skew,kurosis=kurt)) } option(digits=3) ## 가독성을 높이기 위해 소수점 3자리 유지 sapply(data.frame,mystats) -- 추가로 쉽게 이걸로 가능. library(psych) describe(data.frame) ## 2. 독립 검정 ## 카이제곱검정 - categorical + categorical twt=xtabs(~col1+col2,data=data.frame) chisq.test(twt) ## 만약 cell의 20%가 기대빈도가 5보다 작다면 ## fisher's exact test. fisher.test(twt) ## cochran-mantel-haenszel test (CMH) ## 세번째 범주형 변수가 두 이항변수의 조건부 연관성을 검정할 때 twt=xtabs(~col1+col2+col3,data=data.frame) mantelhaen.test(twt) ## col3 가 영향을 준다고 가정. ## 3. 상관계수 ## 연속형 상관계수 cor(data.frame) ## method : pearson, spearman cor.test(data.frame[,3],data.frame[,5]) ## 3번,5번 상관관계 검정 ## 편상관계수 구하기 ## 첫번째(1)와 두번째(5)의 상관관계가 (2,3,6)의 제어조건을 받고 상관과계가 있나를 확인한다. library(ggm) pcor(c(1,5,2,3,6),stats:cov(states)) ## 4. t.test 그룹 비교 검정 ## 독립검정 - 범주~연속 t.test(group~cont,data=data.frame)) ## 짝검정 - 연속,연속 with(data.frame,t.test(col1,col2,paired=T)) ## 5. 셋 이상의 그룹 비교 ## 독립성,등분산,정규성을 만족한다면 anova summary(aov(cont~group,data=data.frame)) ## 가정을 만족못한다 - 비모수 kruskal kruskal.test(cont~group,data=data.frame) ## 6. 설문지 신뢰도 ## 특징. 1에 가까울수록 성능이 좋다.(보통 0.6~0.8) ## 특징. 변수를 제거했을 때 크론바하 a 값이 커지면 변수를 제외한다. ## 특징. 문항의 수가 a값에 포함되기 때문에 적정한 수의 문항도 있어야한다. library(psych) alpha(data.frame)
mongodb
NoSQL은 초당 데이터가 수십만개씩 쌓이는 서비스에서 많이 사용된다. mongodb은 대표적인 NoSQL프로그램이다. json형식의 document기반으로 데이터를 관리한다.
프로그램이 있고 mongodb전용 cli도 있다. 이번엔 python으로 control 했다.
저장되어있는 data를 가져와서 pandas dataframe으로 바꾸는 과정을 썼다. 보통 data handling은 pandas가 편해서 그렇다. 잘 사용할 수 있는걸로 하면 된다.
데이터를 저장하는 방식은 밑의 selenium 파트에서 할 것이다.
code
## 순서는 다음과 같다. ## library가져오고, db에 접근해서 ## pandas로 만든다. ## library import pandas as pd from pymongo import MongoClient client=MongoClient('localhost',27017) db=client.coin xrp=db.xrp ## coin db의 xrp document를 의미한다. SQL의 table은 mongodb의 colletion이고, ## SQL의 각각의 row들이 mongodb의 document에 해당한다. all_docs=xrp.find() lst=[] for docs in all_docs: data=[docs['year'],docs['start'][1:],docs['high'][1:], docs['low'][1:],docs['end'][1:],docs['volume'][1:],docs['price'][1:]] if data not in lst: lst.append(data) ## 본인이 자주쓰는 data frame을 만드는 방법이 2가지 있다. ## 1. list형식으로 여러 row를 만들어서 한번에 집어넣기. ## 2. dict형식으로 column과 데이터(list형식)를 짝지어 한번에 집어넣기. ## 이번엔 1번 방법으로 했다. df=pd.DataFrame(lst,columns=['year','start','high','low','end','volume','price']) df.to_csv('5Yxrp.csv',index=False)
selenium
mongodb를 사용한 이유다. 코인 시가총액 데이터를 긁어오기 위해서 selenium을 사용했고, 관련 데이터를 저장하기위해서 mongodb를 이용한 것이다.
selenium은 보통 정적인 웹사이트가 아닌 동적인 웹사이트를 parsing할 때 많이 사용한다.
이번 프로젝트 데이터 수집 코드를 가져왔다.
code
## 기본적인 selenium을 활용한 crawl은 인터넷에 많다. ## 이번에는 button을 눌러서 새로운 데이터가 계속 나타나게 하는 작업을 진행했다. ## ripple coin의 5개년 데이터를 가져왔다. ## library from selenium import webdriver import time from pymongo import MongoClient client=MongoClient('localhost', 27017) db=client.coin xrp=db.xrp ## option 들을 보통 넣어주는 편이다. options=webdriver.ChromeOptions() # options.headless=True options.add_argument('window-size=1920x1080') options.add_experimental_option("excludeSwitches", ["enable-logging"]) options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36') browser=webdriver.Chrome('./chromedriver_win32/chromedriver.exe', options=options) url='https://coinmarketcap.com/ko/currencies/xrp/historical-data/' browser.get(url) time.sleep(3) count=0 button='//*[@id="__next"]/div/div[1]/div[2]/div/div[3]/div/div/p[1]/button' y=3500 ## 처음 버튼의 위치다. 자동화중에 이게 계속 보이게 하는게 중요하다. while count!=61: # 선택 browser.execute_script(f'window.scrollTo(0, {y});') browser.find_elements_by_xpath(button)[0].click() # 대기 time.sleep(3) # 조건 ## 한번 클릭에 1개월치 이므로, 1*12*5 즉, 60. 61은 그냥 했다. count+=1 ## selenium은 자동화될때 버튼이 화면에 보여야한다. 중요하다. ## 1500의 높이를 확인했는데 정확하게 원위치가 안돼서 5번의 한번꼴로 높이를 올려주었다. y+=1500 if count%5==0: y+=190 ## 운이 좋게 tbody tag가 하나밖에 없었다. ## find_element_by ~ 는 많다. xpath도 있고, tag name도 있고 그렇다. trs=browser.find_elements_by_css_selector('tbody tr') # print(trs) # print(f"len(trs) : {len(trs)}") for tr in trs: lst=tr.text.split(' ') year=lst[0]+' '+lst[1]+' '+lst[2] start=lst[3] high=lst[4] low=lst[5] end=lst[6] volume=lst[7] price=lst[8] data={ 'year':year, 'start':start, 'high':high, 'low':low, 'end':end, 'volume':volume, 'price':price } # print(data) ## 별거 없다. 하나씩 넣을 땐 insert_one 여러개 한번에 넣을땐 리스트에 모아두고 insert_many. ## 단, 그냥 따옴표는 안되고, "" 쌍따옴표만 되는것 같다. xrp.insert_one(data) time.sleep(7) browser.quit()
코인 자동매매 with aws
유튜브에 코인자동매매 알고리즘과 aws를 이용하여 계속 돌아가게 하는 영상이 있다. 참고
코인 알고리즘은 동영상에서 자세히 설명하므로 넘어가고 aws와 결과에 대해서 간단히 써보겠다.
20210513부터 현재(20210519)까지 진행한 증거를 보여주려고 한다.
본인은 aws를 통한 매매자동화 코딩을 위해 한번 해본것이다.
결론은 돈 잃었다. 일주일정도만 했고 돈도 10000원만 넣고 관심도 없었지만, 막상 잃으니 기분이 좀 그렇다.
result
## 1. 프리티어라고 모두 프리티어가 아니다. ## ec2는 프리티어가 맞다. 월 750시간, 하루 25시간 즉, 계속 돌려도 문제 없다. ## 다만, 데이터 전송의 가격이 든다. $0.001/1GB 가 소비된다. ## 지역의 차이인 것 같다. ## 2. 코인의 결과 ## 본인은 이더리움으로 10000원 넣어 진행했다. ## 돈도 조금 넣었을 뿐 아니라 본인은 원래 주식,코인 이런거에 부정적이라 안한다. ## 다만, aws를 이용한 자동매매라는 것이 재밌어보여서 해본것이다. ## 이번 2주간 진행 후 역시 아니라고 생각했다. ## 20210513 매수하자마자 9977원으로 하락 매도. ## 20210516 10091원으로 매도. ## 20210517 9609원으로 하락 매도.

Streamlit
평소에 코로나 현황 대쉬보드를 만들고 싶어 했다.
역시 처음부터 만들면 힘들지만, streamlit이란 framework로 간단하게 만들어봤다.
그 전에 본인은 docker로 모든 활동을 한다. 왜냐하면 컴퓨터에 이것저것 깔다가 망가지는 경우를 많이 봤다. docker로 했기 때문에 라이브러리 의존성이 유지된다.
docker
## port와 host ## docker는 말 그대로 os위에 얹혀가는 kernel이다. ## 다른 os를 새로 설치할 필요없이 container를 격리 시켜 새로운 환경을 만드는 것이다. ## mac docker run -i -t -p 8888:8888 -p 8000:8000 -p 8501:8501 -v ~/workspace:/workspace --name conda continuumio/miniconda3 /bin/bash ## window docker run -i -t -p 8888:8888 -p 8000:8000 -p 8501:8501 -v C:/Users/{user}/{workspace}:/{workspace} --name {name} continuumio/miniconda3 /bin/bash ## -i 인터랙티브 하게 ## -t terminal과 같은 환경을 사용하겠다. ## -p port 열어놓겠다 host(내 컴퓨터)의 8888포트와 container의 8888포트를 매핑한다. ## 8888 jupyter 8000 flask 8501 streamlit ## -v root/workspace폴더를 container안에 workspace라는 이름으로 넣는다. ## --name container 이름은 conda. ## docker image continuumio/miniconda3 image를 가져와서 실행시킨다. ## bin/bash bin/bash 파일을 실행시킨다.
code
## 본인은 api를 통한 대쉬보드를 만드려고 했기 때문에 requests library가 필요하다. ## 잘 정돈된 code는 https://github.com/98hyun/side/streamlit 에서 볼 수 있다. ## library import streamlit as st import requests import config ## config api_key=config.api_key url=f'https://api.corona-19.kr/korea/country/new/?serviceKey={api_key}' info=f'https://api.corona-19.kr/korea/?serviceKey={api_key}' ## parse info_res=requests.get(info) info_json=info_res.json() ## get data from region select bar def getdata(region): # 신규확진환자수 newcase=res_json[region]['newCase'].strip() ... ## h1 같은 글. st.title("코로나 현황 대쉬보드") ## p 태그 같은 글. st.write(f""" {info_json['updateTime']} """) region=("전국","서울","부산","대구","인천","광주","대전","울산","세종", "경기","강원","충북","충남","전북","전남","경북","경남","제주") ## selectbox 와 sidebar select_region=st.sidebar.selectbox("지역 선택",region) if select_region=='전국': region='korea' result=getdata(region) elif select_region=='서울': region='seoul' result=getdata(region) ... ## kpi setting ## grid kpi1,kpi2,kpi3=st.beta_columns(3) with kpi1: st.markdown("신규확진자 수
",unsafe_allow_html=True) newcase=int(result[0].replace(',','')) if newcase>0: st.markdown(f"▲ {newcase}
",unsafe_allow_html=True) else: st.markdown(f"▼ {newcase}
",unsafe_allow_html=True) ...
API와 TinyDB
code 중에 tinydb로 간단하게 만들었다.
code
from tinydb import TinyDB,Query db=TinyDB('corona_db.json') today_obj=sorted(db.all(),key=lambda k:k['today']) if today_obj[-1]['today']!=today: db.insert(all_result) ## find db.all()
Deploy with GCP
dockerfile 정의가 필요하다. 파일은 여기에 정리해놨다.
20210518 - gcp 유지비가 많이 들어 현재 중단했다.
code
## dockerfile 설명 1. port는 8080 배포 port 2. --server.enableCORS false 뜻는 모르겠다. ## server.yaml 1. runtime: custom ## 내가 정한다는 뜻 2. env: flex ## 정확하게 모르겠다. 그냥 dockerfile하고 연결되어있는 것 같다. ## 순서. 1. gcloud init ## 초기화 필요하다고 한다. 2. gcloud project list ## 확인 3. gcloud config set project {project_name} ## 설정. 4. gcloud config get-value project ## 선택 잘됐는지 확인 5. gcloud app deploy server.yaml ## 이름을 다르게 지정해줘서 따로 파일이름 명시 6. press y, gcloud app browse ## link 확인

plotly embed
20210430 post에서 kaggle notebook으로 google bigquery를 사용할 수 있는 방법을 공부했었다.
이번엔 plotly를 통해 google analytics sample을 가져와 plotly와 matplotlib로 표현하고, blog에 embed해봤다.
자세한 코드는 이 링크를 참고.
code
## https://chart-studio.plotly.com 에서 본인 워크스페이스 만들기. ## 0. 데이터 import. ## 1. trace 추가. 쉽게 말해 plot모양을 정한다. ## 2. style의 general 이용. 다른것도 많다. ## 3. save 후 만들어진 viewer 안에 페이스북,트위터,link,embed 중 embed선택 후 대입.
matplot3d
저번 post의 cluster를 복습하다가 그룹화를 지어 3차원으로 표현하는 방법을 보여주고 싶어 글을 쓴다. 간혹 3d의 그래프를 그릴때가 필요한데 아래처럼 하면 편하다. 이 사이트의 notebook을 공부한 것이니 참고하면 된다.
code
..snip fig = plt.figure(figsize=(10,10)) ax = fig.add_subplot(111, projection='3d') ax.scatter(grouped_km2["Spending Score (1-100)"], grouped_km2["Annual Income (k$)"], grouped_km2["Age"],color=['#2a333f','#939da6','#0f4c81','#be3e35','#70090a','#244747'],alpha=0.5,s=500) # add annotations one by one with a loop for line in range(0,grouped_km.shape[0]): ax.text(grouped_km2['Spending Score (1-100)'][line], grouped_km2['Annual Income (k$)'][line],grouped_km2['Age'][line], s=('Cluster \n'+grouped_km2['Cluster'][line]), horizontalalignment='center', fontsize=12, fontweight='light', fontfamily='serif') ax.set_xlabel("Spending Score (1-100)") ax.set_ylabel("Annual Income (k$)") ax.set_zlabel("Age") fig.text(0.15, .77, '3D Plot: Clusters 시각화', fontsize=15, fontweight='bold', fontproperties=fontprop) fig.text(0.15, .745, 'cluster가 공간에서 차지하는 의미를 파악할 수 있다.', fontsize=12, fontweight='light', fontproperties=fontprop) fig.text(1.172, 0.77, 'Insight', fontsize=15, fontweight='bold', fontfamily='serif') fig.text(1.172, 0.347, ''' cluster들 간에 명확한 구분을 확인할 수 있다. 기업에서 우리는 clusters의 이름을 바꿀 필요가 있다. 그래서 명확하게 알 수 있게 했다. Cluster 0 - 낮은 소비점수, 낮은 수익, 중간 나이대 - 덜 중요한 Cluster 1 - 낮은 소비점수, 높은 수익, 중간 나이대 - Target Cluster 2 - 중간 소비점수, 중간 수익, 젊은 나이대 - 조금 가치가 있다. Cluster 3 - 중간 소비점수, 중간 수익, 높은 나이대 - 별로 가치가 없다. Cluster 4 - 높은 소비점수, 높은 수익, 젊은 나이대 - 매우 가치가 있다. Cluster 5 - 높은 소비점수, 낮은 수익, 젊은 나이대 - 가치가 있다. ''' , fontsize=12, fontweight='light', fontproperties=fontprop) import matplotlib.lines as lines l1 = lines.Line2D([1, 1], [0, 1], transform=fig.transFigure, figure=fig,color='black',lw=0.2) fig.lines.extend([l1]) ..endsnip

R
R로 데이터 공모전에 나갔다. 대전시 창업지원대책마련이라는 주제로 데이터 분석과 시각화를 진행했다.
익숙치 않은 언어로 했는데 문법도 쉽고 얻을수 있는 소스도 많아서 노력많이 하니까 잘 마무리 한 것 같다.
그 중 시각화 할때 몇가지 패턴이 보였는데 공유하려고 한다.
그전에 r 개발환경 설치를 docker와 miniconda를 이용해 쉽게 하는 방법을 공유하고자 한다.
R
## 저번 post에서 docker설치를 포스트했다. docker를 이용한다. ## 현재 위치는 docker 안. ## cli 로 install.packages 하는것보다 어떤게 필요하고 아닌지 잘 모르기때문에 이게 낫다. ## 후에 conda install -c conda-forge r-essential r-base ##
code
## 1. ggplot() -> 기준. aes(x,y)를 통해 미리 정하거나 후에 geom_line 같은 차트에 대입. ## 2. geom_line() -> 차트. geom_bar나 geom_point 같은. ## 3. labs() -> python 에서의 fig 라고 해야하나. plot 밖을 다룰 것들. ## 4. geom_vline 은 구분을 위해 저렇게 + 를 사용해서 잇습니다. ## 5. theme -> 띰 이라고 합니다. ax를 다룰것. python에서 ax,plot 같은 의미같다. ## line chart fig(12,8) ggplot(cases[-c(1),c('DATE','일일확진자')], aes(x=DATE)) + geom_line(aes(y=일일확진자)) + labs(x="Date", y="Number of Cases", title="Distribution of COVID Cases in KOREA")+ geom_vline(aes(xintercept=as.numeric(cases[-c(1),]$DATE[306])),linetype='dashed',color='red')+ geom_vline(aes(xintercept=as.numeric(cases[-c(1),]$DATE[275])),color='red')+ geom_vline(aes(xintercept=as.numeric(cases[-c(1),]$DATE[214])),color='red')+ geom_vline(aes(xintercept=as.numeric(cases[-c(1),]$DATE[92])),color='red')+ geom_text(aes(x=as.POSIXct('2020-02-01'),y=1200,label='Pre코로나'),size=4)+ geom_text(aes(x=as.POSIXct('2020-03-16'),y=1200,label='1차유행'),size=4)+ geom_text(aes(x=as.POSIXct('2020-04-30'),y=1200,label='포스트코로나\n(안정기)'),size=4)+ geom_text(aes(x=as.POSIXct('2020-07-30'),y=1200,label='2차유행'),size=4)+ geom_text(aes(x=as.POSIXct('2020-11-20'),y=1200,label='3차유행'),size=4)+ theme_bw()+ theme(plot.title = element_text(size=22) ,axis.text.x= element_text(size=15), # 빈칸을 하고 싶다면 element_blank() axis.text.y= element_text(size=15), axis.title=element_text(size=18)) ## bar chart ## R은 문자열의 대소가 없는것 같다. 그래서 직접 xtick(x axis text)를 정해줬다. ## scale_fill_gradient 같은 것은 찾아야한다. 보통 cheat sheet 라고 하면 잘 찾을 수 있다. ## https://github.com/rstudio/cheatsheets/blob/master/data-visualization-2.1.pdf fig(12,8) 정보통신$년월=factor(정보통신$년월,levels=c('2020년1월','2020년2월','2020년3월','2020년4월','2020년5월', '2020년6월','2020년7월','2020년8월','2020년9월','2020년10월','2020년11월','2020년12월')) ggplot(정보통신, aes(년월,정보통신업_총액,fill=정보통신업_총액))+ geom_bar(stat="identity", width = 0.5)+ geom_text(aes(label=정보통신업_총액),vjust=-1.2) + scale_fill_gradient(low = "green", high = "red")+ labs(x="년월",y="Volume",title="Bar Chart about Spent Volume with 정보통신")+ theme_bw()+ theme(plot.title = element_text(size=22),axis.text.x= element_text(size=15,angle=90), axis.text.y= element_blank(), axis.title=element_text(size=18))

앙상블
앙상블은 모델들을 모아서 새로운 모델을 예측값을 내는것이다. 배깅과 부스팅 모두 앙상블이다.
앙상블의 장점은 예측력이 올라간다. 여러 모델들 덕분에 일반화가 되고 분산이 감소한다.
다만, 과적합이 될 수 있고 해석력이 없어진다는 것이다. 모델을 합치니 성능이 좋다.
그런데 왜 좋은지 모른다. 나중에 예측을 할 때 '몰라 인공지능이 이렇다는데?' 이렇게 될 수 있다는 뜻이다.
현재 인공지능 혹은 머신러닝에서 제일 중요한 과정은 모델의 해석력을 설명하는 것이다.
하지만, 최근에 데이콘 신용카드 연체자 예측 문제를 푸는데 데이터 전처리를 해도 모델이 문제인지 성능이 향상 되지 않는 것이다. 그래서 모델을 앙상블을 해봤다. 성능이 눈에 띄게 좋아졌다.
일단, 그 코드를 공유하고자 한다. 모델링은 5-fold로 xgboost와 lgbm, random forest를 사용한다.
code
## 종속변수의 분포가 imbalance하기 때문에 계층적 fold로 나눠줬다. ## 아래와 같은 코드를 lgb와 rf에 같이 했다. ## 처음 전처리 가정에서 이상치를 걸러내거나 중복값을 제거하는 등 영향이 있을 것이라 봤지만 ## 모델 성능향상에는 큰 도움이 안됐다. 그래서 문제는 모델을 얼마나 잘 설계하느냐 같았다. .. snip skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) folds=[] for train_idx, valid_idx in skf.split(train, train['credit']): folds.append((train_idx, valid_idx)) xgb_models={} for fold in range(5): print(f'===================================={fold+1}============================================') train_idx, valid_idx = folds[fold] X_train, X_valid, y_train, y_valid = train.drop(['credit'],axis=1).iloc[train_idx].values, train.drop(['credit'],axis=1).iloc[valid_idx].values,\ train['credit'][train_idx].values, train['credit'][valid_idx].values params={ 'objective':'multi:softprob', 'random_state':71, 'n_estimators':1000 } model = XGBClassifier(**params) model.fit(X_train, y_train, eval_set=[(X_train, y_train), (X_valid, y_valid)], eval_metric = 'mlogloss',early_stopping_rounds=30, verbose=100) xgb_models[fold]=model print(f'================================================================================\n\n') ... submit.iloc[:,1:]=0 for fold in range(5): submit.iloc[:,1:] += (lgb_models[fold].predict_proba(test)/15) submit.iloc[:,1:] += (xgb_models[fold].predict_proba(test)/15) submit.iloc[:,1:] += (rf_models[fold].predict_proba(test)/15) submit.head() .. endsnip