Toy Project/축구선수 연봉 예측

축구선수 데이터 전처리

수타. 2023. 6. 27. 18:00

저번에 셀레니움을 활용한 web crawling으로 부터 

다음과 같이 자료들을 얻을 수 있었습니다. 자료들을 열어보면

 

다음과 같이 Player Club Nationality 그리고 각각 선수들의 해당하는 컬럼(위 사진에선 Goals)값이 들어있는것을 확인할 수 있습니다.

 

이후의 전처리는 코랩에서 이루어져 있습니다.

 

이제 코랩에서 파일을 올리기 편하게 하기 위해 Zip으로 압축한뒤 올리고(drive 를 활용해도 무방함)

!unzip -qq "/content/Soccer_Player_data.zip"

주소를 넣고 unzip을 해줍니다.

 

import pandas as pd
#데이터 불러오기
#포지션 공격수,수비수,미드필더,골키퍼
position = ['Forward','Midfielder','Defender','Goalkeeper']
features = ['Goals', 'Assists', 'Appearances', 'Minutes Played', 'Yellow Cards', 'Red Cards', 'Substituted On', 'Substituted Off', 'Shots', 'Shots On Target', 'Hit Woodwork', 'Goals From Header', 'Goals From Penalty', 'Goals From Freekick', 'Offsides', 'Touches', 'Passes', 'Through Balls', 'Crosses', 'Corners Taken', 'Interceptions', 'Blocks', 'Tackles', 'Last Man Tackles', 'Clearances', 'Headed Clearances', 'Aerial Battles Won', 'Own Goals', 'Errors Leading To Goal', 'Penalties Conceded', 'Aerial Battles Lost', 'Clean Sheets', 'Goals Conceded', 'Saves', 'Penalties Saved', 'Punches', 'High Claims', 'Sweeper Clearances', 'Throw Outs', 'Goal Kicks']

#각 데이터를 불러모아 그 csv파일의 이름으로 데이터프레임으로 만들어 주었습니다.
for P in position:
  for F in features:
    #이때 변수명에 space가 들어가는걸 방지하기 위해
    if len(F.split()) != 1 : #공백으로 나눠진다면
      newF = '_'.join(F.split()) #공백이 아닌 언더바로 바꾸기
      globals()['{}_{}'.format(P,newF)] = pd.read_csv('{}_{}.csv'.format(P,F),encoding='utf-8-sig')
      #컬럼명도 공백이 아닌 언더바로 바꾸어줍니다.
      globals()['{}_{}'.format(P,newF)].rename(columns={F:newF},inplace=True)
    else:
      globals()['{}_{}'.format(P,F)] = pd.read_csv('{}_{}.csv'.format(P,F),encoding='utf-8-sig')

이제 데이터를 불러모아 데이터 프레임으로 바꾸는 과정입니다. features값들은 셀레니움에서 추출했으며, 

이때 파일들이 엄청 많기 때문에 한번에 하기 위해 다음과같이 이중 포문을 만들었습니다. 

이때 변수를 데이터프레임명으로 할당하기 이해 globals()함수를 위와 같이 사용했으며,

이때 features값에 'Minutes Played' 와 같이 중간에 spacebar가 들어가 있는것들이있어 if 문을 통해 공백인것들을 언더바로 바꾸어 주었고, 선수들의 다국적인 이름들의 깨지는것을 방지하기 위해 encoding을 해주었습니다.

 

#앞으로 사용할 features 도 공백이 아닌 언더바로 바꾸어 줍니다.
for F in features:
  if len(F.split()) != 1 :
    newF = '_'.join(F.split())
    features[features.index(F)] = newF

또한 앞으로 사용할 features 도 역시 공백이 아닌 언더바로 바꾸어줍니다.

사용할땐 index함수를 사용했지만 그냥 포문에서 enumerate를 쓰는것이 더 깔끔했을거 같습니다.

 

#모두 그이름으로 데이터 프레임이 된 모습을 확인할 수 있고, ',' 때문에 문자인 데이터들을 숫자로 변환해줍니다.
for P in position:
  for F in features:
    if globals()['{}_{}'.format(P,F)][F].dtype == object:
      globals()['{}_{}'.format(P,F)][F] = globals()['{}_{}'.format(P,F)][F].str.replace(pat=r'[^\w]', repl=r'', regex=True) #특수기호 지우기
      globals()['{}_{}'.format(P,F)][F] = pd.to_numeric(globals()['{}_{}'.format(P,F)][F],errors='ignore') #지운후 숫자로 바꿀수있으면 바꾸기
    globals()['{}_{}'.format(P,F)].info()
    print()

데이터 프레임 뒤에 .info()를 붙이면 데이터 프레임내의 데이터들의 Dtype들을 확인할 수 있습니다.

이때 숫자가 아닌 데이터들이 껴있거나 (ex 1,000 ) 숫자라도 문자형으로 받아들일 수 있기 때문에 전처리를 해줍니다.

 

#모든 데이터들 Player 기준으로 통합
for P in position:
  #일단 Position으로된 이름의 데이터프레임을 featuers 1,2번만 따서 만들고
  globals()['{}'.format(P)] = pd.merge(globals()['{}_{}'.format(P,features[0])],globals()['{}_{}'.format(P,features[1])], how='outer', on=['Player','Club','Nationality'])
  for F in features[2:] :
    #그뒤로는 쭉 붙입니다.
    globals()['{}'.format(P)] = pd.merge(globals()['{}'.format(P)],globals()['{}_{}'.format(P,F)], how='outer', on=['Player','Club','Nationality'])

모든데이터들을 merge함수를 통해 한 데이터 프레임으로 만들어줍니다.

 

#Merge함수를 쓸때 how 파라미터를 outer로 했기 때문에 없는데이터는 결측치로 됩니다. 결측치 처리
#그리고 결측치라는것은 없다는것 이기 때문에 0으로 대체해 줍니다. ex) Goal = Nan 은 골을 넣지않아 데이터가 없다는 의미
Goalkeeper.fillna(0, inplace=True)
Forward.fillna(0, inplace=True)
Midfielder.fillna(0, inplace=True)
Defender.fillna(0, inplace=True)

이제 결측치를 0으로 바꾸어 주는데 위에 merge함수에서 how 파라미터 값을 outer를 쓴이유가 여기에 있습니다.

가령 수비수의 골 같은경우 데이터가 있는 선수가 흔치 않습니다. 그래서 없는선수들이 많은데 이를 inner를 사용하여 있는 선수들만 한다면 db가 얼마 남지 않을것 입니다. 따라 outer로 처리해서 없는 데이터들을 nan으로 만든후 0으로 바꾸어줍니다.

 

to_csv함수를 통해 csv형태로 저장한 후 열어 확인해보면 다음과 같이 이름을 기준으로 모든 features데이터들이 들어가 있는것을 확인할 수 있습니다.

 

연봉데이터들도 같은 과정을 통해 전처리 후 , merge함수로 묶어주었습니다.

 


지금까지가 전처리 과정이고 , 이후 OpenML을 사용해 어떤 모델이 성능이 잘 나올지 확인해 보았지만, 성능이 생각보다 잘 나오지 않아 추가적으로 3년치의 데이터와 풋볼 게임 메니져 사이트를 통해 레이팅, 잠재력, 나이, 주급데이터등을 더 가져온 후, 머신러닝을 진행하였습니다. 머신러닝 내용은 다음에 이어 포스팅하겠습니다.