python
복사
import pandas as pd
import matplotlib.pyplot as plt
players = pd.read_csv('players.csv')
players_valuations = pd.read_csv('player_valuations.csv')
players.csv : 선수 정보(이름, 생년월일, 출생 국가/도시, 포지션, 소속 클럽 등)를 담은 파일player_valuations.csv : 선수 가치(연도별 시장 가치)를 담은 파일두 CSV 파일을 불러와 각각 players, players_valuations DataFrame으로 저장했습니다.
python
복사
players.info()
players.columns
players.info() : 데이터프레임의 행(row), 열(column) 개수 및 각 컬럼의 결측치(null) 존재 여부, 데이터 타입 등을 확인합니다.players.columns : 열 이름을 확인합니다.python
복사
cols = [
'player_id', 'first_name', 'last_name', 'name', 'last_season',
'current_club_id', 'player_code', 'country_of_birth', 'city_of_birth',
'country_of_citizenship', 'date_of_birth', 'sub_position', 'position'
]
players = players[cols]
players.isnull().sum() # 각 컬럼별 결측치 개수 확인
players.dropna(inplace=True) # 결측치 제거
players.isnull().sum()
python
복사
def count_value(df, column):
count = len(df[column].unique())
print(f'Total {column}: {count}')
ads = ['player_id', 'current_club_id', 'country_of_citizenship']
for column in ads:
count_value(players, column)
player_id, current_club_id, country_of_citizenship 컬럼 각각에 대해 고유값의 개수를 확인합니다.country_of_citizenship에 대한 값들을 보면, 선수들이 어느 나라 출신이 가장 많은지, 대략적인 분포 파악이 가능합니다.python
복사
players_valuations.info()
players_valuations.describe()
players_valuations.info()를 통해 행/열 개수, 결측치 여부, 데이터 타입 확인players_valuations.describe()로 기초 통계값(평균, 표준편차, 최소/최대, 사분위수 등) 확인python
복사
mean_ = players_valuations['market_value_in_eur'].mean()
over_mean = len(players_valuations[players_valuations['market_value_in_eur'] > mean_])
total = len(players_valuations)
print(f'percentile of player over mean: {over_mean / total * 100: .2f}%')
players 와 players_valuations 병합(Merge)python
복사
players_with_val = pd.merge(players, players_valuations, on='player_id')
player_id를 기준으로 두 DataFrame을 Inner Join(기본 설정)하여 하나의 DataFrame(players_with_val)으로 통합python
복사
players_with_val['dateyear'] = players_with_val['date'].apply(lambda x: int(x[:4]))
players_with_val['age'] = players_with_val['dateyear'] - players_with_val['date_of_birth'].apply(lambda x: int(x[:4]))
players_with_val.drop_duplicates(['player_id', 'dateyear'], keep='last', inplace=True)
date 컬럼에서 연도(year)만 추출하여 dateyear라는 새로운 컬럼 생성date_of_birth)와 비교하여 선수 나이(age) 계산python
복사
cols_2 = [
'player_id', 'current_club_id_y', 'first_name', 'last_name', 'name', 'last_season_x',
'country_of_citizenship', 'city_of_birth', 'position', 'sub_position',
'dateyear', 'age', 'market_value_in_eur'
]
players_with_val = players_with_val[cols_2]
players_with_val.rename(columns={
'current_club_id_y':'current_club_id',
'last_season_x':'last_season'
}, inplace=True)
_y, _x) 등을 적절한 이름으로 변경python
복사
players_with_val_2022 = players_with_val[
(players_with_val['dateyear'] == 2022) & (players_with_val['last_season'] == 2022)
]
players_with_val_2022['market_value_in_eur'] = players_with_val_2022['market_value_in_eur'].rank(method='min', ascending=False)
players_with_val_2022.sort_values(by='market_value_in_eur')
sort_values(by='market_value_in_eur')로 정렬 후, 상위 선수가 누구인지 살펴봅니다.python
복사
plt.figure(figsize=(8, 6))
plt.boxplot(players_with_val['market_value_in_eur'])
plt.show()
python
복사
plt.figure(figsize=(8, 6))
players_with_val.groupby('market_value_in_eur')['player_id'].count().plot()
plt.show()
python
복사
sum_per_year = players_with_val.groupby('dateyear')['market_value_in_eur'].sum()
max_per_year = players_with_val.groupby('dateyear')['market_value_in_eur'].max()
python
복사
plt.figure(figsize=(8, 6))
plt.plot(sum_per_year.index, sum_per_year.values)
plt.xticks(rotation=45)
plt.show()
plt.figure(figsize=(8, 6))
plt.plot(max_per_year.index, max_per_year.values)
plt.xticks(rotation=45)
plt.show()
plt.plot으로 연도별 추세를 확인python
복사
players_with_val = players_with_val[(players_with_val['dateyear'] >= 2013) & (players_with_val['dateyear'] < 2023)]
years = sorted(players_with_val['dateyear'].unique())
num_plots = len(years)
num_rows = 4
num_cols = (num_plots + 3) // 4
fig, axes = plt.subplots(num_rows, num_cols, figsize=(12, 8))
axes = axes.flatten()
for i, year in enumerate(years):
market_values = players_with_val[players_with_val['dateyear'] == year]['market_value_in_eur'].values
ax = axes[i]
ax.boxplot(market_values)
ax.set_title(year)
for j in range(num_plots, num_rows * num_cols):
fig.delaxes(axes[j])
plt.tight_layout()
plt.show()
python
복사
age_marget_values = players_with_val.groupby('age')['market_value_in_eur'].mean()
age_marget_values.plot(kind='bar')
python
복사
filtered_df = players_with_val[players_with_val['age'] <= 35]
age_marget_values = filtered_df.groupby('age')['market_value_in_eur'].mean()
sorted_values = age_marget_values.sort_values(ascending=False)
top_5_intervals = sorted_values.head(5).index
python
복사
top_players = filtered_df.groupby('age').apply(lambda x: x.loc[x['market_value_in_eur'].idxmax()]['name'])
age_marget_values.plot(kind='bar', color=colors)
for i, value in enumerate(age_marget_values):
age = age_marget_values.index[i]
top_player = top_players[age]
# 막대 위에 최고 가치 선수 이름 표시
plt.text(i, value, top_player, ha='center', va='bottom', color='black')
plt.xticks(rotation=45)
plt.show()
python
복사
position_market_values = players_with_val.groupby('position')['market_value_in_eur'].mean()
position_market_values.plot(kind='bar')
python
복사
position_market_values = players_with_val.groupby(['position', 'dateyear'])['market_value_in_eur'].mean().reset_index()
for position in position_market_values['position'].unique():
position_data = position_market_values[position_market_values['position'] == position]
plt.plot(position_data['dateyear'], position_data['market_value_in_eur'], label=position)
plt.legend()
plt.show()
아래 코드는 포지션 → 세부 포지션(sub_position)으로 나누어 연도별 추이를 분석하려는 예시입니다.
사용 시, 실제로는 sub_position에 대한 데이터가 존재해야 하며, 코드의 변수명 확인이 필요합니다.
python
복사
country_player_counts = players_with_val.drop_duplicates('player_id')['country_of_citizenship'].value_counts()
top_10_countries = country_player_counts.head(10)
top_10_countries.plot(kind='bar')
player_id 기준) 확인python
복사
from geopy.geocoders import Nominatim
import time
geolocator = Nominatim(user_agent='my-app')
locations = []
errors = []
countries = country_player_counts.index
for country in countries:
try:
location = geolocator.geocode(country)
time.sleep(0.3)
latitude = location.latitude
longitude = location.longitude
locations.append((country, latitude, longitude))
except Exception as e:
errors.append(country)
continue
locations_df = pd.DataFrame(locations)
locations_df.rename(columns={0: 'country', 1: 'latitude', 2: 'longitude'}, inplace=True)
locations_df['player_counts'] = locations_df['country'].apply(lambda x: country_player_counts[x])
Nominatim(geopy) 라이브러리를 통해 국가 이름으로 위도/경도를 검색errors 리스트) 처리python
복사
import plotly.express as px
fig = px.density_mapbox(
locations_df, lat='latitude', lon='longitude',
z='player_counts', radius=15,
center=dict(lat=0, lon=180), zoom=0,
mapbox_style='stamen-terrian'
)
fig.show()
python
복사
cities_in_england = players_with_val[players_with_val['country_of_citizenship'] == 'England'].drop_duplicates('player_id')['city_of_birth']
england_players_count = cities_in_england.value_counts()
top_10_city_in_england = england_players_count.head(10)
plt.figure(figsize=(12,8))
top_10_city_in_england.plot(kind='bar')
for i, value in enumerate(top_10_city_in_england):
plt.text(i, value, str(value), ha='center', va='bottom')
plt.show()
dateyear), 나이(age) 등을 파생 변수로 생성해 시계열·분포 분석에 활용.geopy, plotly 등 외부 라이브러리를 활용하여, 선수들이 출생한 국가(도시) 분포를 지도로 표시할 수 있습니다.np.log)을 적용해 데이터의 편향(skewness)을 줄여볼 수도 있습니다.이상으로, 축구 선수 데이터를 활용한 기본적인 전처리, EDA, 시각화 과정을 정리해보았습니다. 데이터분석 과정에서 가장 중요한 것은 “무엇을 알고 싶은지”입니다.