1. 데이터 불러오기
# seaborn의 penguins 데이터셋 로드
penguins_df = pl.from_pandas(sns.load_dataset('penguins'))
penguins_df
# seaborn의 penguins 데이터셋 로드
penguins_df = pl.from_pandas(sns.load_dataset('penguins'))
penguins_df
2 데이터 정보 확인
print("데이터 기본 정보:")
print(penguins_df.glimpse())
print(penguins_df.describe())
print("데이터 기본 정보:")
print(penguins_df.glimpse())
print(penguins_df.describe())
데이터 기본 정보:
Rows: 344
Columns: 7
$ species <str> 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie'
$ island <str> 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen'
$ bill_length_mm <f64> 39.1, 39.5, 40.3, None, 36.7, 39.3, 38.9, 39.2, 34.1, 42.0
$ bill_depth_mm <f64> 18.7, 17.4, 18.0, None, 19.3, 20.6, 17.8, 19.6, 18.1, 20.2
$ flipper_length_mm <f64> 181.0, 186.0, 195.0, None, 193.0, 190.0, 181.0, 195.0, 193.0, 190.0
$ body_mass_g <f64> 3750.0, 3800.0, 3250.0, None, 3450.0, 3650.0, 3625.0, 4675.0, 3475.0, 4250.0
$ sex <str> 'Male', 'Female', 'Female', None, 'Female', 'Male', 'Female', 'Male', None, None
None
shape: (9, 8)
┌────────────┬─────────┬───────────┬─────────────┬─────────────┬─────────────┬────────────┬────────┐
│ statistic ┆ species ┆ island ┆ bill_length ┆ bill_depth_ ┆ flipper_len ┆ body_mass_ ┆ sex │
│ --- ┆ --- ┆ --- ┆ _mm ┆ mm ┆ gth_mm ┆ g ┆ --- │
│ str ┆ str ┆ str ┆ --- ┆ --- ┆ --- ┆ --- ┆ str │
│ ┆ ┆ ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ │
╞════════════╪═════════╪═══════════╪═════════════╪═════════════╪═════════════╪════════════╪════════╡
│ count ┆ 344 ┆ 344 ┆ 342.0 ┆ 342.0 ┆ 342.0 ┆ 342.0 ┆ 333 │
│ null_count ┆ 0 ┆ 0 ┆ 2.0 ┆ 2.0 ┆ 2.0 ┆ 2.0 ┆ 11 │
│ mean ┆ null ┆ null ┆ 43.92193 ┆ 17.15117 ┆ 200.915205 ┆ 4201.75438 ┆ null │
│ ┆ ┆ ┆ ┆ ┆ ┆ 6 ┆ │
│ std ┆ null ┆ null ┆ 5.459584 ┆ 1.974793 ┆ 14.061714 ┆ 801.954536 ┆ null │
│ min ┆ Adelie ┆ Biscoe ┆ 32.1 ┆ 13.1 ┆ 172.0 ┆ 2700.0 ┆ Female │
│ 25% ┆ null ┆ null ┆ 39.2 ┆ 15.6 ┆ 190.0 ┆ 3550.0 ┆ null │
│ 50% ┆ null ┆ null ┆ 44.5 ┆ 17.3 ┆ 197.0 ┆ 4050.0 ┆ null │
│ 75% ┆ null ┆ null ┆ 48.5 ┆ 18.7 ┆ 213.0 ┆ 4750.0 ┆ null │
│ max ┆ Gentoo ┆ Torgersen ┆ 59.6 ┆ 21.5 ┆ 231.0 ┆ 6300.0 ┆ Male │
└────────────┴─────────┴───────────┴─────────────┴─────────────┴─────────────┴────────────┴────────┘
데이터 기본 정보:
Rows: 344
Columns: 7
$ species <str> 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie', 'Adelie'
$ island <str> 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen', 'Torgersen'
$ bill_length_mm <f64> 39.1, 39.5, 40.3, None, 36.7, 39.3, 38.9, 39.2, 34.1, 42.0
$ bill_depth_mm <f64> 18.7, 17.4, 18.0, None, 19.3, 20.6, 17.8, 19.6, 18.1, 20.2
$ flipper_length_mm <f64> 181.0, 186.0, 195.0, None, 193.0, 190.0, 181.0, 195.0, 193.0, 190.0
$ body_mass_g <f64> 3750.0, 3800.0, 3250.0, None, 3450.0, 3650.0, 3625.0, 4675.0, 3475.0, 4250.0
$ sex <str> 'Male', 'Female', 'Female', None, 'Female', 'Male', 'Female', 'Male', None, None
None
shape: (9, 8)
┌────────────┬─────────┬───────────┬─────────────┬─────────────┬─────────────┬────────────┬────────┐
│ statistic ┆ species ┆ island ┆ bill_length ┆ bill_depth_ ┆ flipper_len ┆ body_mass_ ┆ sex │
│ --- ┆ --- ┆ --- ┆ _mm ┆ mm ┆ gth_mm ┆ g ┆ --- │
│ str ┆ str ┆ str ┆ --- ┆ --- ┆ --- ┆ --- ┆ str │
│ ┆ ┆ ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ │
╞════════════╪═════════╪═══════════╪═════════════╪═════════════╪═════════════╪════════════╪════════╡
│ count ┆ 344 ┆ 344 ┆ 342.0 ┆ 342.0 ┆ 342.0 ┆ 342.0 ┆ 333 │
│ null_count ┆ 0 ┆ 0 ┆ 2.0 ┆ 2.0 ┆ 2.0 ┆ 2.0 ┆ 11 │
│ mean ┆ null ┆ null ┆ 43.92193 ┆ 17.15117 ┆ 200.915205 ┆ 4201.75438 ┆ null │
│ ┆ ┆ ┆ ┆ ┆ ┆ 6 ┆ │
│ std ┆ null ┆ null ┆ 5.459584 ┆ 1.974793 ┆ 14.061714 ┆ 801.954536 ┆ null │
│ min ┆ Adelie ┆ Biscoe ┆ 32.1 ┆ 13.1 ┆ 172.0 ┆ 2700.0 ┆ Female │
│ 25% ┆ null ┆ null ┆ 39.2 ┆ 15.6 ┆ 190.0 ┆ 3550.0 ┆ null │
│ 50% ┆ null ┆ null ┆ 44.5 ┆ 17.3 ┆ 197.0 ┆ 4050.0 ┆ null │
│ 75% ┆ null ┆ null ┆ 48.5 ┆ 18.7 ┆ 213.0 ┆ 4750.0 ┆ null │
│ max ┆ Gentoo ┆ Torgersen ┆ 59.6 ┆ 21.5 ┆ 231.0 ┆ 6300.0 ┆ Male │
└────────────┴─────────┴───────────┴─────────────┴─────────────┴─────────────┴────────────┴────────┘
print(penguins_df.shape)
print(penguins_df.shape)
3. 데이터 전처리
3.1 결측값 확인
print("결측치 개수:")
print(penguins_df.null_count())
print("결측치 개수:")
print(penguins_df.null_count())
결측치 개수:
shape: (1, 7)
┌─────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬─────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 │
╞═════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪═════╡
│ 0 ┆ 0 ┆ 2 ┆ 2 ┆ 2 ┆ 2 ┆ 11 │
└─────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴─────┘
결측치 개수:
shape: (1, 7)
┌─────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬─────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 │
╞═════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪═════╡
│ 0 ┆ 0 ┆ 2 ┆ 2 ┆ 2 ┆ 2 ┆ 11 │
└─────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴─────┘
3.2 데이터 삭제
penguins_df = penguins_df.drop_nulls('bill_length_mm')
penguins_df
penguins_df = penguins_df.drop_nulls('bill_length_mm')
penguins_df
3.3 결측치 채우기
penguins_df = penguins_df.with_columns((pl.col('sex').fill_null(penguins_df['sex'].mode()[0])).alias('sex'))
penguins_df
penguins_df = penguins_df.with_columns((pl.col('sex').fill_null(penguins_df['sex'].mode()[0])).alias('sex'))
penguins_df
print("결측치 개수:")
print(titanic_df.null_count())
print("결측치 개수:")
print(titanic_df.null_count())
결측치 개수:
shape: (1, 7)
┌─────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬─────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 │
╞═════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪═════╡
│ 0 ┆ 0 ┆ 0 ┆ 0 ┆ 0 ┆ 0 ┆ 0 │
└─────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴─────┘
결측치 개수:
shape: (1, 7)
┌─────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬─────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 │
╞═════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪═════╡
│ 0 ┆ 0 ┆ 0 ┆ 0 ┆ 0 ┆ 0 ┆ 0 │
└─────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴─────┘
4. 종별 신체 특성 분석
species_analysis = (
penguins_df.group_by('species')
.agg([
pl.col('bill_length_mm').mean().alias('평균_부리길이'),
pl.col('bill_depth_mm').mean().alias('평균_부리깊이'),
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.count().alias('개체_수')
])
)
print("종별 신체 특성 분석:")
print(species_analysis)
species_analysis = (
penguins_df.group_by('species')
.agg([
pl.col('bill_length_mm').mean().alias('평균_부리길이'),
pl.col('bill_depth_mm').mean().alias('평균_부리깊이'),
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.count().alias('개체_수')
])
)
print("종별 신체 특성 분석:")
print(species_analysis)
종별 신체 특성 분석:
shape: (3, 6)
┌───────────┬───────────────┬───────────────┬───────────────┬─────────────┬─────────┐
│ species ┆ 평균_부리길이 ┆ 평균_부리깊이 ┆ 평균_날개길이 ┆ 평균_체중 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ u32 │
╞═══════════╪═══════════════╪═══════════════╪═══════════════╪═════════════╪═════════╡
│ Chinstrap ┆ 48.833824 ┆ 18.420588 ┆ 195.823529 ┆ 3733.088235 ┆ 68 │
│ Adelie ┆ 38.791391 ┆ 18.346358 ┆ 189.953642 ┆ 3700.662252 ┆ 151 │
│ Gentoo ┆ 47.504878 ┆ 14.982114 ┆ 217.186992 ┆ 5076.01626 ┆ 123 │
└───────────┴───────────────┴───────────────┴───────────────┴─────────────┴─────────┘
종별 신체 특성 분석:
shape: (3, 6)
┌───────────┬───────────────┬───────────────┬───────────────┬─────────────┬─────────┐
│ species ┆ 평균_부리길이 ┆ 평균_부리깊이 ┆ 평균_날개길이 ┆ 평균_체중 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ u32 │
╞═══════════╪═══════════════╪═══════════════╪═══════════════╪═════════════╪═════════╡
│ Chinstrap ┆ 48.833824 ┆ 18.420588 ┆ 195.823529 ┆ 3733.088235 ┆ 68 │
│ Adelie ┆ 38.791391 ┆ 18.346358 ┆ 189.953642 ┆ 3700.662252 ┆ 151 │
│ Gentoo ┆ 47.504878 ┆ 14.982114 ┆ 217.186992 ┆ 5076.01626 ┆ 123 │
└───────────┴───────────────┴───────────────┴───────────────┴─────────────┴─────────┘
# 그래프 크기 설정
plt.figure(figsize=(15, 10))
# 2x2 서브플롯 생성
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 10))
# 데이터 준비
species = species_analysis['species']
measurements = ['평균_부리길이', '평균_부리깊이', '평균_날개길이', '평균_체중']
colors = ['lightblue', 'lightgreen', 'lightcoral']
# 1. 부리길이 (좌상단)
bars1 = ax1.bar(species, species_analysis['평균_부리길이'], color=colors)
ax1.set_title('종별 평균 부리 길이')
ax1.set_ylabel('부리 길이 (mm)')
for bar in bars1:
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm',
ha='center', va='bottom')
# 2. 부리깊이 (우상단)
bars2 = ax2.bar(species, species_analysis['평균_부리깊이'], color=colors)
ax2.set_title('종별 평균 부리 깊이')
ax2.set_ylabel('부리 깊이 (mm)')
for bar in bars2:
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm',
ha='center', va='bottom')
# 3. 날개길이 (좌하단)
bars3 = ax3.bar(species, species_analysis['평균_날개길이'], color=colors)
ax3.set_title('종별 평균 날개 길이')
ax3.set_ylabel('날개 길이 (mm)')
for bar in bars3:
height = bar.get_height()
ax3.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm',
ha='center', va='bottom')
# 4. 체중 (우하단)
bars4 = ax4.bar(species, species_analysis['평균_체중'], color=colors)
ax4.set_title('종별 평균 체중')
ax4.set_ylabel('체중 (g)')
for bar in bars4:
height = bar.get_height()
ax4.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.0f}g',
ha='center', va='bottom')
# 전체 제목 추가
plt.suptitle('펭귄 종별 신체 특성 분석', size=15, y=1.02)
# 각 그래프에 개체 수 정보 추가
for ax in [ax1, ax2, ax3, ax4]:
for i, species_name in enumerate(species):
count = species_analysis.filter(pl.col('species') == species_name)['개체_수'].to_numpy()[0]
ax.text(i, 0, f'(n={count})', ha='center', va='top')
plt.tight_layout()
plt.show()
# 그래프 크기 설정
plt.figure(figsize=(15, 10))
# 2x2 서브플롯 생성
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 10))
# 데이터 준비
species = species_analysis['species']
measurements = ['평균_부리길이', '평균_부리깊이', '평균_날개길이', '평균_체중']
colors = ['lightblue', 'lightgreen', 'lightcoral']
# 1. 부리길이 (좌상단)
bars1 = ax1.bar(species, species_analysis['평균_부리길이'], color=colors)
ax1.set_title('종별 평균 부리 길이')
ax1.set_ylabel('부리 길이 (mm)')
for bar in bars1:
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm',
ha='center', va='bottom')
# 2. 부리깊이 (우상단)
bars2 = ax2.bar(species, species_analysis['평균_부리깊이'], color=colors)
ax2.set_title('종별 평균 부리 깊이')
ax2.set_ylabel('부리 깊이 (mm)')
for bar in bars2:
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm',
ha='center', va='bottom')
# 3. 날개길이 (좌하단)
bars3 = ax3.bar(species, species_analysis['평균_날개길이'], color=colors)
ax3.set_title('종별 평균 날개 길이')
ax3.set_ylabel('날개 길이 (mm)')
for bar in bars3:
height = bar.get_height()
ax3.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm',
ha='center', va='bottom')
# 4. 체중 (우하단)
bars4 = ax4.bar(species, species_analysis['평균_체중'], color=colors)
ax4.set_title('종별 평균 체중')
ax4.set_ylabel('체중 (g)')
for bar in bars4:
height = bar.get_height()
ax4.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.0f}g',
ha='center', va='bottom')
# 전체 제목 추가
plt.suptitle('펭귄 종별 신체 특성 분석', size=15, y=1.02)
# 각 그래프에 개체 수 정보 추가
for ax in [ax1, ax2, ax3, ax4]:
for i, species_name in enumerate(species):
count = species_analysis.filter(pl.col('species') == species_name)['개체_수'].to_numpy()[0]
ax.text(i, 0, f'(n={count})', ha='center', va='top')
plt.tight_layout()
plt.show()
5. 서식지별 펭귄 분포
island_analysis = (
penguins_df.group_by(['island', 'species'])
.agg([
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.count().alias('개체_수')
])
.sort(['island', 'species'])
)
print("서식지별 펭귄 분포:")
print(island_analysis)
island_analysis = (
penguins_df.group_by(['island', 'species'])
.agg([
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.count().alias('개체_수')
])
.sort(['island', 'species'])
)
print("서식지별 펭귄 분포:")
print(island_analysis)
서식지별 펭귄 분포:
shape: (5, 5)
┌───────────┬───────────┬─────────────┬───────────────┬─────────┐
│ island ┆ species ┆ 평균_체중 ┆ 평균_날개길이 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ u32 │
╞═══════════╪═══════════╪═════════════╪═══════════════╪═════════╡
│ Biscoe ┆ Adelie ┆ 3709.659091 ┆ 188.795455 ┆ 44 │
│ Biscoe ┆ Gentoo ┆ 5076.01626 ┆ 217.186992 ┆ 123 │
│ Dream ┆ Adelie ┆ 3688.392857 ┆ 189.732143 ┆ 56 │
│ Dream ┆ Chinstrap ┆ 3733.088235 ┆ 195.823529 ┆ 68 │
│ Torgersen ┆ Adelie ┆ 3706.372549 ┆ 191.196078 ┆ 51 │
└───────────┴───────────┴─────────────┴───────────────┴─────────┘
서식지별 펭귄 분포:
shape: (5, 5)
┌───────────┬───────────┬─────────────┬───────────────┬─────────┐
│ island ┆ species ┆ 평균_체중 ┆ 평균_날개길이 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ u32 │
╞═══════════╪═══════════╪═════════════╪═══════════════╪═════════╡
│ Biscoe ┆ Adelie ┆ 3709.659091 ┆ 188.795455 ┆ 44 │
│ Biscoe ┆ Gentoo ┆ 5076.01626 ┆ 217.186992 ┆ 123 │
│ Dream ┆ Adelie ┆ 3688.392857 ┆ 189.732143 ┆ 56 │
│ Dream ┆ Chinstrap ┆ 3733.088235 ┆ 195.823529 ┆ 68 │
│ Torgersen ┆ Adelie ┆ 3706.372549 ┆ 191.196078 ┆ 51 │
└───────────┴───────────┴─────────────┴───────────────┴─────────┘
# 그래프 크기 설정
plt.figure(figsize=(15, 6))
# 데이터 준비
islands = ['Biscoe', 'Dream', 'Torgersen']
species_colors = {
'Adelie': 'lightblue',
'Gentoo': 'lightgreen',
'Chinstrap': 'lightcoral'
}
# 각 섬별로 종의 분포를 보여주는 그룹화된 막대 그래프
data = {}
for row in island_analysis.iter_rows(named=True):
island = row['island']
species = row['species']
count = row['개체_수']
if island not in data:
data[island] = {}
data[island][species] = count
x = np.arange(len(islands))
width = 0.25
multiplier = 0
# 각 종별로 막대 그래프 생성
for species, color in species_colors.items():
counts = []
for island in islands:
counts.append(data[island].get(species, 0))
offset = width * multiplier
plt.bar(x + offset, counts, width, label=species, color=color)
# 데이터 레이블 추가
for i, count in enumerate(counts):
if count > 0:
plt.text(x[i] + offset, count, str(count),
ha='center', va='bottom')
multiplier += 1
# 그래프 꾸미기
plt.title('서식지별 펭귄 종 분포', pad=20, size=15)
plt.xlabel('서식지')
plt.ylabel('개체 수')
plt.xticks(x + width, islands)
plt.legend(title='종')
plt.tight_layout()
plt.show()
# 체중과 날개길이를 보여주는 산점도
plt.figure(figsize=(10, 6))
for species, color in species_colors.items():
species_data = island_analysis.filter(pl.col('species') == species)
plt.scatter(species_data['평균_체중'],
species_data['평균_날개길이'],
label=species,
color=color,
s=100) # 점 크기
# 각 점에 섬 이름 표시
for row in species_data.iter_rows(named=True):
plt.annotate(row['island'],
(row['평균_체중'], row['평균_날개길이']),
xytext=(5, 5), textcoords='offset points')
plt.title('서식지-종별 체중과 날개길이 관계', pad=20, size=15)
plt.xlabel('평균 체중 (g)')
plt.ylabel('평균 날개길이 (mm)')
plt.legend(title='종')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 그래프 크기 설정
plt.figure(figsize=(15, 6))
# 데이터 준비
islands = ['Biscoe', 'Dream', 'Torgersen']
species_colors = {
'Adelie': 'lightblue',
'Gentoo': 'lightgreen',
'Chinstrap': 'lightcoral'
}
# 각 섬별로 종의 분포를 보여주는 그룹화된 막대 그래프
data = {}
for row in island_analysis.iter_rows(named=True):
island = row['island']
species = row['species']
count = row['개체_수']
if island not in data:
data[island] = {}
data[island][species] = count
x = np.arange(len(islands))
width = 0.25
multiplier = 0
# 각 종별로 막대 그래프 생성
for species, color in species_colors.items():
counts = []
for island in islands:
counts.append(data[island].get(species, 0))
offset = width * multiplier
plt.bar(x + offset, counts, width, label=species, color=color)
# 데이터 레이블 추가
for i, count in enumerate(counts):
if count > 0:
plt.text(x[i] + offset, count, str(count),
ha='center', va='bottom')
multiplier += 1
# 그래프 꾸미기
plt.title('서식지별 펭귄 종 분포', pad=20, size=15)
plt.xlabel('서식지')
plt.ylabel('개체 수')
plt.xticks(x + width, islands)
plt.legend(title='종')
plt.tight_layout()
plt.show()
# 체중과 날개길이를 보여주는 산점도
plt.figure(figsize=(10, 6))
for species, color in species_colors.items():
species_data = island_analysis.filter(pl.col('species') == species)
plt.scatter(species_data['평균_체중'],
species_data['평균_날개길이'],
label=species,
color=color,
s=100) # 점 크기
# 각 점에 섬 이름 표시
for row in species_data.iter_rows(named=True):
plt.annotate(row['island'],
(row['평균_체중'], row['평균_날개길이']),
xytext=(5, 5), textcoords='offset points')
plt.title('서식지-종별 체중과 날개길이 관계', pad=20, size=15)
plt.xlabel('평균 체중 (g)')
plt.ylabel('평균 날개길이 (mm)')
plt.legend(title='종')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
6. 성별에 따른 신체 특성
sex_analysis = (
penguins_df.group_by(['species', 'sex'])
.agg([
pl.col('bill_length_mm').mean().alias('평균_부리길이'),
pl.col('bill_depth_mm').mean().alias('평균_부리깊이'),
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.count().alias('개체_수')
])
.sort(['species', 'sex'])
)
print("성별 신체 특성 분석:")
print(sex_analysis)
sex_analysis = (
penguins_df.group_by(['species', 'sex'])
.agg([
pl.col('bill_length_mm').mean().alias('평균_부리길이'),
pl.col('bill_depth_mm').mean().alias('평균_부리깊이'),
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.count().alias('개체_수')
])
.sort(['species', 'sex'])
)
print("성별 신체 특성 분석:")
print(sex_analysis)
성별 신체 특성 분석:
shape: (6, 7)
┌───────────┬────────┬───────────────┬───────────────┬───────────────┬─────────────┬─────────┐
│ species ┆ sex ┆ 평균_부리길이 ┆ 평균_부리깊이 ┆ 평균_날개길이 ┆ 평균_체중 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ u32 │
╞═══════════╪════════╪═══════════════╪═══════════════╪═══════════════╪═════════════╪═════════╡
│ Adelie ┆ Female ┆ 37.257534 ┆ 17.621918 ┆ 187.794521 ┆ 3368.835616 ┆ 73 │
│ Adelie ┆ Male ┆ 40.226923 ┆ 19.024359 ┆ 191.974359 ┆ 4011.217949 ┆ 78 │
│ Chinstrap ┆ Female ┆ 46.573529 ┆ 17.588235 ┆ 191.735294 ┆ 3527.205882 ┆ 34 │
│ Chinstrap ┆ Male ┆ 51.094118 ┆ 19.252941 ┆ 199.911765 ┆ 3938.970588 ┆ 34 │
│ Gentoo ┆ Female ┆ 45.563793 ┆ 14.237931 ┆ 212.706897 ┆ 4679.741379 ┆ 58 │
│ Gentoo ┆ Male ┆ 49.236923 ┆ 15.646154 ┆ 221.184615 ┆ 5429.615385 ┆ 65 │
└───────────┴────────┴───────────────┴───────────────┴───────────────┴─────────────┴─────────┘
성별 신체 특성 분석:
shape: (6, 7)
┌───────────┬────────┬───────────────┬───────────────┬───────────────┬─────────────┬─────────┐
│ species ┆ sex ┆ 평균_부리길이 ┆ 평균_부리깊이 ┆ 평균_날개길이 ┆ 평균_체중 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ u32 │
╞═══════════╪════════╪═══════════════╪═══════════════╪═══════════════╪═════════════╪═════════╡
│ Adelie ┆ Female ┆ 37.257534 ┆ 17.621918 ┆ 187.794521 ┆ 3368.835616 ┆ 73 │
│ Adelie ┆ Male ┆ 40.226923 ┆ 19.024359 ┆ 191.974359 ┆ 4011.217949 ┆ 78 │
│ Chinstrap ┆ Female ┆ 46.573529 ┆ 17.588235 ┆ 191.735294 ┆ 3527.205882 ┆ 34 │
│ Chinstrap ┆ Male ┆ 51.094118 ┆ 19.252941 ┆ 199.911765 ┆ 3938.970588 ┆ 34 │
│ Gentoo ┆ Female ┆ 45.563793 ┆ 14.237931 ┆ 212.706897 ┆ 4679.741379 ┆ 58 │
│ Gentoo ┆ Male ┆ 49.236923 ┆ 15.646154 ┆ 221.184615 ┆ 5429.615385 ┆ 65 │
└───────────┴────────┴───────────────┴───────────────┴───────────────┴─────────────┴─────────┘
# 2x2 서브플롯 생성
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
# 데이터 준비
species = sex_analysis['species'].unique()
bar_width = 0.35
x = np.arange(len(species))
# 색상 설정
female_color = 'lightpink'
male_color = 'lightblue'
# 1. 부리 길이 (좌상단)
females = sex_analysis.filter(pl.col('sex') == 'Female')
males = sex_analysis.filter(pl.col('sex') == 'Male')
def plot_comparison(ax, female_data, male_data, column, title, ylabel):
bars1 = ax.bar(x - bar_width/2, female_data[column], bar_width,
label='Female', color=female_color)
bars2 = ax.bar(x + bar_width/2, male_data[column], bar_width,
label='Male', color=male_color)
ax.set_title(title)
ax.set_ylabel(ylabel)
ax.set_xticks(x)
ax.set_xticklabels(species)
# 데이터 레이블 추가
for bars in [bars1, bars2]:
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}', ha='center', va='bottom')
# 개체 수 표시
if ax == ax1: # 첫 번째 그래프에만 범례 표시
ax.legend()
for i, spec in enumerate(species):
f_count = female_data.filter(pl.col('species') == spec)['개체_수'].item()
m_count = male_data.filter(pl.col('species') == spec)['개체_수'].item()
ax.text(i, 0, f'F:{f_count}\nM:{m_count}', ha='center', va='top')
# 각 특성별 그래프 그리기
plot_comparison(ax1, females, males, '평균_부리길이', '성별 평균 부리 길이', '부리 길이 (mm)')
plot_comparison(ax2, females, males, '평균_부리깊이', '성별 평균 부리 깊이', '부리 깊이 (mm)')
plot_comparison(ax3, females, males, '평균_날개길이', '성별 평균 날개 길이', '날개 길이 (mm)')
plot_comparison(ax4, females, males, '평균_체중', '성별 평균 체중', '체중 (g)')
plt.suptitle('펭귄 종별 성별 신체 특성 비교', size=15, y=1.02)
plt.tight_layout()
plt.show()
# 2x2 서브플롯 생성
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
# 데이터 준비
species = sex_analysis['species'].unique()
bar_width = 0.35
x = np.arange(len(species))
# 색상 설정
female_color = 'lightpink'
male_color = 'lightblue'
# 1. 부리 길이 (좌상단)
females = sex_analysis.filter(pl.col('sex') == 'Female')
males = sex_analysis.filter(pl.col('sex') == 'Male')
def plot_comparison(ax, female_data, male_data, column, title, ylabel):
bars1 = ax.bar(x - bar_width/2, female_data[column], bar_width,
label='Female', color=female_color)
bars2 = ax.bar(x + bar_width/2, male_data[column], bar_width,
label='Male', color=male_color)
ax.set_title(title)
ax.set_ylabel(ylabel)
ax.set_xticks(x)
ax.set_xticklabels(species)
# 데이터 레이블 추가
for bars in [bars1, bars2]:
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}', ha='center', va='bottom')
# 개체 수 표시
if ax == ax1: # 첫 번째 그래프에만 범례 표시
ax.legend()
for i, spec in enumerate(species):
f_count = female_data.filter(pl.col('species') == spec)['개체_수'].item()
m_count = male_data.filter(pl.col('species') == spec)['개체_수'].item()
ax.text(i, 0, f'F:{f_count}\nM:{m_count}', ha='center', va='top')
# 각 특성별 그래프 그리기
plot_comparison(ax1, females, males, '평균_부리길이', '성별 평균 부리 길이', '부리 길이 (mm)')
plot_comparison(ax2, females, males, '평균_부리깊이', '성별 평균 부리 깊이', '부리 깊이 (mm)')
plot_comparison(ax3, females, males, '평균_날개길이', '성별 평균 날개 길이', '날개 길이 (mm)')
plot_comparison(ax4, females, males, '평균_체중', '성별 평균 체중', '체중 (g)')
plt.suptitle('펭귄 종별 성별 신체 특성 비교', size=15, y=1.02)
plt.tight_layout()
plt.show()
7. 체중 구간별 분석
mass_analysis = (
penguins_df.with_columns([
pl.col('body_mass_g')
.cut(breaks=[2000, 3000, 4000, 5000, 6000, 7000],
labels=['0-2kg','2-3kg', '3-4kg', '4-5kg', '5-6kg', '6-7kg','7+kg'])
.alias('weight_group')
])
.group_by(['weight_group', 'species'])
.agg([
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.count().alias('개체_수')
])
.sort(['weight_group', 'species'])
)
print("체중 구간별 분석:")
print(mass_analysis)
mass_analysis = (
penguins_df.with_columns([
pl.col('body_mass_g')
.cut(breaks=[2000, 3000, 4000, 5000, 6000, 7000],
labels=['0-2kg','2-3kg', '3-4kg', '4-5kg', '5-6kg', '6-7kg','7+kg'])
.alias('weight_group')
])
.group_by(['weight_group', 'species'])
.agg([
pl.col('flipper_length_mm').mean().alias('평균_날개길이'),
pl.count().alias('개체_수')
])
.sort(['weight_group', 'species'])
)
print("체중 구간별 분석:")
print(mass_analysis)
체중 구간별 분석:
shape: (10, 4)
┌──────────────┬───────────┬───────────────┬─────────┐
│ weight_group ┆ species ┆ 평균_날개길이 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- │
│ cat ┆ str ┆ f64 ┆ u32 │
╞══════════════╪═══════════╪═══════════════╪═════════╡
│ 2-3kg ┆ Adelie ┆ 185.222222 ┆ 9 │
│ 2-3kg ┆ Chinstrap ┆ 189.5 ┆ 2 │
│ 3-4kg ┆ Adelie ┆ 188.831776 ┆ 107 │
│ 3-4kg ┆ Chinstrap ┆ 194.019608 ┆ 51 │
│ 3-4kg ┆ Gentoo ┆ 208.0 ┆ 1 │
│ 4-5kg ┆ Adelie ┆ 194.6 ┆ 35 │
│ 4-5kg ┆ Chinstrap ┆ 202.8 ┆ 15 │
│ 4-5kg ┆ Gentoo ┆ 213.459016 ┆ 61 │
│ 5-6kg ┆ Gentoo ┆ 220.915254 ┆ 59 │
│ 6-7kg ┆ Gentoo ┆ 225.5 ┆ 2 │
└──────────────┴───────────┴───────────────┴─────────┘
체중 구간별 분석:
shape: (10, 4)
┌──────────────┬───────────┬───────────────┬─────────┐
│ weight_group ┆ species ┆ 평균_날개길이 ┆ 개체_수 │
│ --- ┆ --- ┆ --- ┆ --- │
│ cat ┆ str ┆ f64 ┆ u32 │
╞══════════════╪═══════════╪═══════════════╪═════════╡
│ 2-3kg ┆ Adelie ┆ 185.222222 ┆ 9 │
│ 2-3kg ┆ Chinstrap ┆ 189.5 ┆ 2 │
│ 3-4kg ┆ Adelie ┆ 188.831776 ┆ 107 │
│ 3-4kg ┆ Chinstrap ┆ 194.019608 ┆ 51 │
│ 3-4kg ┆ Gentoo ┆ 208.0 ┆ 1 │
│ 4-5kg ┆ Adelie ┆ 194.6 ┆ 35 │
│ 4-5kg ┆ Chinstrap ┆ 202.8 ┆ 15 │
│ 4-5kg ┆ Gentoo ┆ 213.459016 ┆ 61 │
│ 5-6kg ┆ Gentoo ┆ 220.915254 ┆ 59 │
│ 6-7kg ┆ Gentoo ┆ 225.5 ┆ 2 │
└──────────────┴───────────┴───────────────┴─────────┘
# 그래프 크기 설정
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
# 1. 체중 구간별 개체 수 분포
weight_species = mass_analysis.pivot(
values='개체_수',
columns='species',
index='weight_group'
)
species_colors = {
'Adelie': 'lightblue',
'Chinstrap': 'lightgreen',
'Gentoo': 'lightcoral'
}
for species in species_list:
counts = []
for group in weight_groups:
filtered = mass_analysis.filter(
(pl.col('weight_group') == group) &
(pl.col('species') == species)
)
count = filtered['개체_수'].item() if len(filtered) > 0 else 0
counts.append(count)
ax1.bar(weight_groups, counts, label=species,
alpha=0.7, color=species_colors[species])
ax1.set_title('체중 구간별 종 분포')
ax1.set_xlabel('체중 구간')
ax1.set_ylabel('개체 수')
ax1.legend(title='종')
ax1.tick_params(axis='x', rotation=45)
# 2. 체중 구간별 날개 길이
for species in ['Adelie', 'Chinstrap', 'Gentoo']:
species_data = mass_analysis.filter(pl.col('species') == species)
if len(species_data) > 0:
ax2.plot(species_data['weight_group'],
species_data['평균_날개길이'],
'o-', label=species,
color=species_colors[species],
linewidth=2,
markersize=8)
ax2.set_title('체중 구간별 평균 날개 길이')
ax2.set_xlabel('체중 구간')
ax2.set_ylabel('평균 날개 길이 (mm)')
ax2.legend(title='종')
ax2.grid(True, alpha=0.3)
ax2.tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
# 그래프 크기 설정
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
# 1. 체중 구간별 개체 수 분포
weight_species = mass_analysis.pivot(
values='개체_수',
columns='species',
index='weight_group'
)
species_colors = {
'Adelie': 'lightblue',
'Chinstrap': 'lightgreen',
'Gentoo': 'lightcoral'
}
for species in species_list:
counts = []
for group in weight_groups:
filtered = mass_analysis.filter(
(pl.col('weight_group') == group) &
(pl.col('species') == species)
)
count = filtered['개체_수'].item() if len(filtered) > 0 else 0
counts.append(count)
ax1.bar(weight_groups, counts, label=species,
alpha=0.7, color=species_colors[species])
ax1.set_title('체중 구간별 종 분포')
ax1.set_xlabel('체중 구간')
ax1.set_ylabel('개체 수')
ax1.legend(title='종')
ax1.tick_params(axis='x', rotation=45)
# 2. 체중 구간별 날개 길이
for species in ['Adelie', 'Chinstrap', 'Gentoo']:
species_data = mass_analysis.filter(pl.col('species') == species)
if len(species_data) > 0:
ax2.plot(species_data['weight_group'],
species_data['평균_날개길이'],
'o-', label=species,
color=species_colors[species],
linewidth=2,
markersize=8)
ax2.set_title('체중 구간별 평균 날개 길이')
ax2.set_xlabel('체중 구간')
ax2.set_ylabel('평균 날개 길이 (mm)')
ax2.legend(title='종')
ax2.grid(True, alpha=0.3)
ax2.tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
8. 상관관계 분석
correlations = penguins_df.select([
pl.corr('bill_length_mm', 'bill_depth_mm').alias('부리길이_깊이_상관계수'),
pl.corr('bill_length_mm', 'flipper_length_mm').alias('부리_날개_상관계수'),
pl.corr('body_mass_g', 'flipper_length_mm').alias('체중_날개_상관계수'),
pl.corr('body_mass_g', 'bill_length_mm').alias('체중_부리길이_상관계수')
])
print("신체 특성간 상관관계:")
print(correlations)
correlations = penguins_df.select([
pl.corr('bill_length_mm', 'bill_depth_mm').alias('부리길이_깊이_상관계수'),
pl.corr('bill_length_mm', 'flipper_length_mm').alias('부리_날개_상관계수'),
pl.corr('body_mass_g', 'flipper_length_mm').alias('체중_날개_상관계수'),
pl.corr('body_mass_g', 'bill_length_mm').alias('체중_부리길이_상관계수')
])
print("신체 특성간 상관관계:")
print(correlations)
신체 특성간 상관관계:
shape: (1, 4)
┌────────────────────────┬────────────────────┬────────────────────┬────────────────────────┐
│ 부리길이_깊이_상관계수 ┆ 부리_날개_상관계수 ┆ 체중_날개_상관계수 ┆ 체중_부리길이_상관계수 │
│ --- ┆ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f64 ┆ f64 │
╞════════════════════════╪════════════════════╪════════════════════╪════════════════════════╡
│ -0.235053 ┆ 0.656181 ┆ 0.871202 ┆ 0.59511 │
└────────────────────────┴────────────────────┴────────────────────┴────────────────────────┘
신체 특성간 상관관계:
shape: (1, 4)
┌────────────────────────┬────────────────────┬────────────────────┬────────────────────────┐
│ 부리길이_깊이_상관계수 ┆ 부리_날개_상관계수 ┆ 체중_날개_상관계수 ┆ 체중_부리길이_상관계수 │
│ --- ┆ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f64 ┆ f64 │
╞════════════════════════╪════════════════════╪════════════════════╪════════════════════════╡
│ -0.235053 ┆ 0.656181 ┆ 0.871202 ┆ 0.59511 │
└────────────────────────┴────────────────────┴────────────────────┴────────────────────────┘
# 한글 폰트 설정
plt.rc('font', family='NanumGothic')
plt.rcParams['axes.unicode_minus'] = False
# 그래프 크기 설정
plt.figure(figsize=(10, 8))
# 상관계수 행렬 생성
features = ['부리길이', '부리깊이', '날개길이', '체중']
corr_matrix = np.array([
[1.0, correlations['부리길이_깊이_상관계수'][0],
correlations['부리_날개_상관계수'][0],
correlations['체중_부리길이_상관계수'][0]],
[correlations['부리길이_깊이_상관계수'][0], 1.0,
-0.1, -0.1],
[correlations['부리_날개_상관계수'][0], -0.1,
1.0, correlations['체중_날개_상관계수'][0]],
[correlations['체중_부리길이_상관계수'][0], -0.1,
correlations['체중_날개_상관계수'][0], 1.0]
])
# 히트맵 생성
sns.heatmap(corr_matrix,
annot=True, # 값 표시
fmt='.3f', # 소수점 3자리
cmap='RdYlBu_r', # 색상 맵
xticklabels=features,
yticklabels=features,
center=0, # 중앙값 (색상 기준)
vmin=-1, vmax=1, # 값의 범위
square=True) # 정사각형 셀
plt.title('펭귄 신체 특성 간의 상관관계', pad=20)
plt.tight_layout()
plt.show()
# 추가로 산점도 행렬 그리기
plt.figure(figsize=(12, 12))
columns = ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']
pd_df = penguins_df.select(columns).to_pandas()
pd_df.columns = features
sns.pairplot(pd_df, diag_kind='kde')
plt.suptitle('신체 특성 간의 산점도 행렬', y=1.02, size=15)
plt.show()
# 한글 폰트 설정
plt.rc('font', family='NanumGothic')
plt.rcParams['axes.unicode_minus'] = False
# 그래프 크기 설정
plt.figure(figsize=(10, 8))
# 상관계수 행렬 생성
features = ['부리길이', '부리깊이', '날개길이', '체중']
corr_matrix = np.array([
[1.0, correlations['부리길이_깊이_상관계수'][0],
correlations['부리_날개_상관계수'][0],
correlations['체중_부리길이_상관계수'][0]],
[correlations['부리길이_깊이_상관계수'][0], 1.0,
-0.1, -0.1],
[correlations['부리_날개_상관계수'][0], -0.1,
1.0, correlations['체중_날개_상관계수'][0]],
[correlations['체중_부리길이_상관계수'][0], -0.1,
correlations['체중_날개_상관계수'][0], 1.0]
])
# 히트맵 생성
sns.heatmap(corr_matrix,
annot=True, # 값 표시
fmt='.3f', # 소수점 3자리
cmap='RdYlBu_r', # 색상 맵
xticklabels=features,
yticklabels=features,
center=0, # 중앙값 (색상 기준)
vmin=-1, vmax=1, # 값의 범위
square=True) # 정사각형 셀
plt.title('펭귄 신체 특성 간의 상관관계', pad=20)
plt.tight_layout()
plt.show()
# 추가로 산점도 행렬 그리기
plt.figure(figsize=(12, 12))
columns = ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']
pd_df = penguins_df.select(columns).to_pandas()
pd_df.columns = features
sns.pairplot(pd_df, diag_kind='kde')
plt.suptitle('신체 특성 간의 산점도 행렬', y=1.02, size=15)
plt.show()
9. 종별 부리 형태 분석
bill_shape_analysis = (
penguins_df.with_columns([
(pl.col('bill_length_mm') / pl.col('bill_depth_mm')).alias('bill_ratio')
])
.group_by('species')
.agg([
pl.col('bill_ratio').mean().alias('평균_부리비율'),
pl.col('bill_ratio').std().alias('부리비율_표준편차'),
pl.col('bill_length_mm').mean().alias('평균_부리길이'),
pl.col('bill_depth_mm').mean().alias('평균_부리깊이')
])
)
print("종별 부리 형태 분석:")
print(bill_shape_analysis)
bill_shape_analysis = (
penguins_df.with_columns([
(pl.col('bill_length_mm') / pl.col('bill_depth_mm')).alias('bill_ratio')
])
.group_by('species')
.agg([
pl.col('bill_ratio').mean().alias('평균_부리비율'),
pl.col('bill_ratio').std().alias('부리비율_표준편차'),
pl.col('bill_length_mm').mean().alias('평균_부리길이'),
pl.col('bill_depth_mm').mean().alias('평균_부리깊이')
])
)
print("종별 부리 형태 분석:")
print(bill_shape_analysis)
종별 부리 형태 분석:
shape: (3, 5)
┌───────────┬───────────────┬───────────────────┬───────────────┬───────────────┐
│ species ┆ 평균_부리비율 ┆ 부리비율_표준편차 ┆ 평균_부리길이 ┆ 평균_부리깊이 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════════╪═══════════════╪═══════════════════╪═══════════════╪═══════════════╡
│ Gentoo ┆ 3.175592 ┆ 0.170828 ┆ 47.504878 ┆ 14.982114 │
│ Adelie ┆ 2.119726 ┆ 0.154594 ┆ 38.791391 ┆ 18.346358 │
│ Chinstrap ┆ 2.653756 ┆ 0.146894 ┆ 48.833824 ┆ 18.420588 │
└───────────┴───────────────┴───────────────────┴───────────────┴───────────────┘
종별 부리 형태 분석:
shape: (3, 5)
┌───────────┬───────────────┬───────────────────┬───────────────┬───────────────┐
│ species ┆ 평균_부리비율 ┆ 부리비율_표준편차 ┆ 평균_부리길이 ┆ 평균_부리깊이 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════════╪═══════════════╪═══════════════════╪═══════════════╪═══════════════╡
│ Gentoo ┆ 3.175592 ┆ 0.170828 ┆ 47.504878 ┆ 14.982114 │
│ Adelie ┆ 2.119726 ┆ 0.154594 ┆ 38.791391 ┆ 18.346358 │
│ Chinstrap ┆ 2.653756 ┆ 0.146894 ┆ 48.833824 ┆ 18.420588 │
└───────────┴───────────────┴───────────────────┴───────────────┴───────────────┘
# 한글 폰트 설정
plt.rc('font', family='NanumGothic')
plt.rcParams['axes.unicode_minus'] = False
# 그래프 크기 설정
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# 첫 번째 그래프: 부리 길이와 깊이의 비교
x = np.arange(len(bill_shape_analysis)) # range를 numpy array로 변경
width = 0.35
# 막대 그래프: 부리 길이와 깊이
bars1 = ax1.bar(x - width/2, bill_shape_analysis['평균_부리길이'],
width, label='평균 부리 길이', color='lightblue')
bars2 = ax1.bar(x + width/2, bill_shape_analysis['평균_부리깊이'],
width, label='평균 부리 깊이', color='lightgreen')
# 데이터 레이블 추가
def add_labels(bars):
for bar in bars:
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm', ha='center', va='bottom')
add_labels(bars1)
add_labels(bars2)
ax1.set_title('종별 부리 길이와 깊이 비교')
ax1.set_ylabel('길이 (mm)')
ax1.set_xticks(x)
ax1.set_xticklabels(bill_shape_analysis['species'])
ax1.legend()
# 두 번째 그래프: 부리 비율과 표준편차
ratios = bill_shape_analysis['평균_부리비율'].to_numpy()
stds = bill_shape_analysis['부리비율_표준편차'].to_numpy()
# 막대 그래프: 평균 부리 비율
bars3 = ax2.bar(x, ratios, width,
label='평균 부리 비율(길이/깊이)', color='lightcoral')
# 오차 막대 추가
ax2.errorbar(x, ratios, yerr=stds, fmt='none', color='black',
capsize=5, label='표준편차')
# 데이터 레이블 추가
for i, (ratio, std) in enumerate(zip(ratios, stds)):
ax2.text(i, ratio, f'비율: {ratio:.2f}\n(±{std:.2f})',
ha='center', va='bottom')
ax2.set_title('종별 부리 비율과 변동성')
ax2.set_ylabel('부리 비율 (길이/깊이)')
ax2.set_xticks(x)
ax2.set_xticklabels(bill_shape_analysis['species'])
ax2.legend()
plt.tight_layout()
plt.show()
# 한글 폰트 설정
plt.rc('font', family='NanumGothic')
plt.rcParams['axes.unicode_minus'] = False
# 그래프 크기 설정
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# 첫 번째 그래프: 부리 길이와 깊이의 비교
x = np.arange(len(bill_shape_analysis)) # range를 numpy array로 변경
width = 0.35
# 막대 그래프: 부리 길이와 깊이
bars1 = ax1.bar(x - width/2, bill_shape_analysis['평균_부리길이'],
width, label='평균 부리 길이', color='lightblue')
bars2 = ax1.bar(x + width/2, bill_shape_analysis['평균_부리깊이'],
width, label='평균 부리 깊이', color='lightgreen')
# 데이터 레이블 추가
def add_labels(bars):
for bar in bars:
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}mm', ha='center', va='bottom')
add_labels(bars1)
add_labels(bars2)
ax1.set_title('종별 부리 길이와 깊이 비교')
ax1.set_ylabel('길이 (mm)')
ax1.set_xticks(x)
ax1.set_xticklabels(bill_shape_analysis['species'])
ax1.legend()
# 두 번째 그래프: 부리 비율과 표준편차
ratios = bill_shape_analysis['평균_부리비율'].to_numpy()
stds = bill_shape_analysis['부리비율_표준편차'].to_numpy()
# 막대 그래프: 평균 부리 비율
bars3 = ax2.bar(x, ratios, width,
label='평균 부리 비율(길이/깊이)', color='lightcoral')
# 오차 막대 추가
ax2.errorbar(x, ratios, yerr=stds, fmt='none', color='black',
capsize=5, label='표준편차')
# 데이터 레이블 추가
for i, (ratio, std) in enumerate(zip(ratios, stds)):
ax2.text(i, ratio, f'비율: {ratio:.2f}\n(±{std:.2f})',
ha='center', va='bottom')
ax2.set_title('종별 부리 비율과 변동성')
ax2.set_ylabel('부리 비율 (길이/깊이)')
ax2.set_xticks(x)
ax2.set_xticklabels(bill_shape_analysis['species'])
ax2.legend()
plt.tight_layout()
plt.show()
10. 서식지별 체중 분포
island_mass_distribution = (
penguins_df.group_by('island')
.agg([
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.col('body_mass_g').median().alias('중앙값_체중'),
pl.col('body_mass_g').std().alias('체중_표준편차'),
pl.col('body_mass_g').quantile(0.25).alias('체중_1사분위'),
pl.col('body_mass_g').quantile(0.75).alias('체중_3사분위')
])
)
print("서식지별 체중 분포:")
print(island_mass_distribution)
island_mass_distribution = (
penguins_df.group_by('island')
.agg([
pl.col('body_mass_g').mean().alias('평균_체중'),
pl.col('body_mass_g').median().alias('중앙값_체중'),
pl.col('body_mass_g').std().alias('체중_표준편차'),
pl.col('body_mass_g').quantile(0.25).alias('체중_1사분위'),
pl.col('body_mass_g').quantile(0.75).alias('체중_3사분위')
])
)
print("서식지별 체중 분포:")
print(island_mass_distribution)
서식지별 체중 분포:
shape: (3, 6)
┌───────────┬─────────────┬─────────────┬───────────────┬──────────────┬──────────────┐
│ island ┆ 평균_체중 ┆ 중앙값_체중 ┆ 체중_표준편차 ┆ 체중_1사분위 ┆ 체중_3사분위 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════════╪═════════════╪═════════════╪═══════════════╪══════════════╪══════════════╡
│ Torgersen ┆ 3706.372549 ┆ 3700.0 ┆ 445.10794 ┆ 3350.0 ┆ 4000.0 │
│ Biscoe ┆ 4716.017964 ┆ 4775.0 ┆ 782.855743 ┆ 4200.0 ┆ 5350.0 │
│ Dream ┆ 3712.903226 ┆ 3687.5 ┆ 416.644112 ┆ 3400.0 ┆ 3950.0 │
└───────────┴─────────────┴─────────────┴───────────────┴──────────────┴──────────────┘
서식지별 체중 분포:
shape: (3, 6)
┌───────────┬─────────────┬─────────────┬───────────────┬──────────────┬──────────────┐
│ island ┆ 평균_체중 ┆ 중앙값_체중 ┆ 체중_표준편차 ┆ 체중_1사분위 ┆ 체중_3사분위 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════════╪═════════════╪═════════════╪═══════════════╪══════════════╪══════════════╡
│ Torgersen ┆ 3706.372549 ┆ 3700.0 ┆ 445.10794 ┆ 3350.0 ┆ 4000.0 │
│ Biscoe ┆ 4716.017964 ┆ 4775.0 ┆ 782.855743 ┆ 4200.0 ┆ 5350.0 │
│ Dream ┆ 3712.903226 ┆ 3687.5 ┆ 416.644112 ┆ 3400.0 ┆ 3950.0 │
└───────────┴─────────────┴─────────────┴───────────────┴──────────────┴──────────────┘
# 그래프 크기 설정
plt.figure(figsize=(12, 6))
# 박스플롯과 바이올린 플롯을 결합
ax = plt.gca()
# 데이터 준비
islands = island_mass_distribution['island'].to_list()
pos = np.arange(len(islands))
box_data = []
for island in islands:
data = penguins_df.filter(pl.col('island') == island)['body_mass_g'].to_list()
box_data.append(data)
# 바이올린 플롯
violin = plt.violinplot(box_data, positions=pos, showmeans=True)
# 색상 설정
for pc in violin['bodies']:
pc.set_facecolor('lightblue')
pc.set_alpha(0.7)
violin['cmeans'].set_color('red')
# 박스플롯
bp = plt.boxplot(box_data, positions=pos, widths=0.3,
patch_artist=True, showfliers=True)
# 박스플롯 색상 설정
for box in bp['boxes']:
box.set_facecolor('lightgreen')
box.set_alpha(0.5)
# 평균값과 중앙값 레이블 추가
for i, island in enumerate(islands):
avg = island_mass_distribution['평균_체중'][i]
med = island_mass_distribution['중앙값_체중'][i]
std = island_mass_distribution['체중_표준편차'][i]
plt.text(i, avg + std, f'평균: {avg:.0f}g\n중앙값: {med:.0f}g\n표준편차: {std:.0f}g',
ha='center', va='bottom')
# 그래프 꾸미기
plt.title('서식지별 펭귄 체중 분포', pad=20, size=15)
plt.xlabel('서식지')
plt.ylabel('체중 (g)')
plt.xticks(pos, islands)
# 범례 추가
handles = [
plt.Rectangle((0,0),1,1, facecolor='lightblue', alpha=0.7),
plt.Rectangle((0,0),1,1, facecolor='lightgreen', alpha=0.5),
plt.Line2D([0], [0], color='red', linestyle='-', linewidth=2)
]
plt.legend(handles, ['전체 분포', '사분위 범위', '평균값'],
loc='upper right')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 그래프 크기 설정
plt.figure(figsize=(12, 6))
# 박스플롯과 바이올린 플롯을 결합
ax = plt.gca()
# 데이터 준비
islands = island_mass_distribution['island'].to_list()
pos = np.arange(len(islands))
box_data = []
for island in islands:
data = penguins_df.filter(pl.col('island') == island)['body_mass_g'].to_list()
box_data.append(data)
# 바이올린 플롯
violin = plt.violinplot(box_data, positions=pos, showmeans=True)
# 색상 설정
for pc in violin['bodies']:
pc.set_facecolor('lightblue')
pc.set_alpha(0.7)
violin['cmeans'].set_color('red')
# 박스플롯
bp = plt.boxplot(box_data, positions=pos, widths=0.3,
patch_artist=True, showfliers=True)
# 박스플롯 색상 설정
for box in bp['boxes']:
box.set_facecolor('lightgreen')
box.set_alpha(0.5)
# 평균값과 중앙값 레이블 추가
for i, island in enumerate(islands):
avg = island_mass_distribution['평균_체중'][i]
med = island_mass_distribution['중앙값_체중'][i]
std = island_mass_distribution['체중_표준편차'][i]
plt.text(i, avg + std, f'평균: {avg:.0f}g\n중앙값: {med:.0f}g\n표준편차: {std:.0f}g',
ha='center', va='bottom')
# 그래프 꾸미기
plt.title('서식지별 펭귄 체중 분포', pad=20, size=15)
plt.xlabel('서식지')
plt.ylabel('체중 (g)')
plt.xticks(pos, islands)
# 범례 추가
handles = [
plt.Rectangle((0,0),1,1, facecolor='lightblue', alpha=0.7),
plt.Rectangle((0,0),1,1, facecolor='lightgreen', alpha=0.5),
plt.Line2D([0], [0], color='red', linestyle='-', linewidth=2)
]
plt.legend(handles, ['전체 분포', '사분위 범위', '평균값'],
loc='upper right')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
11. 이상치 분석
def find_outliers(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return df.filter(
(pl.col(column) < lower_bound) | (pl.col(column) > upper_bound)
)
outliers = {}
for column in ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']:
outliers[column] = penguins_df.group_by('species').map_groups(
lambda group: find_outliers(group, column)
)
print("이상치 분석:")
for column, outlier_data in outliers.items():
print(f"\n{column} 이상치:")
print(outlier_data)
def find_outliers(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return df.filter(
(pl.col(column) < lower_bound) | (pl.col(column) > upper_bound)
)
outliers = {}
for column in ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']:
outliers[column] = penguins_df.group_by('species').map_groups(
lambda group: find_outliers(group, column)
)
print("이상치 분석:")
for column, outlier_data in outliers.items():
print(f"\n{column} 이상치:")
print(outlier_data)
이상치 분석:
bill_length_mm 이상치:
shape: (2, 7)
┌───────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬────────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═══════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪════════╡
│ Gentoo ┆ Biscoe ┆ 59.6 ┆ 17.0 ┆ 230.0 ┆ 6050.0 ┆ Male │
│ Chinstrap ┆ Dream ┆ 58.0 ┆ 17.8 ┆ 181.0 ┆ 3700.0 ┆ Female │
└───────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴────────┘
bill_depth_mm 이상치:
shape: (1, 7)
┌─────────┬───────────┬────────────────┬───────────────┬───────────────────┬─────────────┬──────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═════════╪═══════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪══════╡
│ Adelie ┆ Torgersen ┆ 46.0 ┆ 21.5 ┆ 194.0 ┆ 4200.0 ┆ Male │
└─────────┴───────────┴────────────────┴───────────────┴───────────────────┴─────────────┴──────┘
flipper_length_mm 이상치:
shape: (2, 7)
┌─────────┬───────────┬────────────────┬───────────────┬───────────────────┬─────────────┬────────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═════════╪═══════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪════════╡
│ Adelie ┆ Biscoe ┆ 37.9 ┆ 18.6 ┆ 172.0 ┆ 3150.0 ┆ Female │
│ Adelie ┆ Torgersen ┆ 44.1 ┆ 18.0 ┆ 210.0 ┆ 4000.0 ┆ Male │
└─────────┴───────────┴────────────────┴───────────────┴───────────────────┴─────────────┴────────┘
body_mass_g 이상치:
shape: (2, 7)
┌───────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬────────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═══════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪════════╡
│ Chinstrap ┆ Dream ┆ 52.0 ┆ 20.7 ┆ 210.0 ┆ 4800.0 ┆ Male │
│ Chinstrap ┆ Dream ┆ 46.9 ┆ 16.6 ┆ 192.0 ┆ 2700.0 ┆ Female │
└───────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴────────┘
이상치 분석:
bill_length_mm 이상치:
shape: (2, 7)
┌───────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬────────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═══════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪════════╡
│ Gentoo ┆ Biscoe ┆ 59.6 ┆ 17.0 ┆ 230.0 ┆ 6050.0 ┆ Male │
│ Chinstrap ┆ Dream ┆ 58.0 ┆ 17.8 ┆ 181.0 ┆ 3700.0 ┆ Female │
└───────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴────────┘
bill_depth_mm 이상치:
shape: (1, 7)
┌─────────┬───────────┬────────────────┬───────────────┬───────────────────┬─────────────┬──────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═════════╪═══════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪══════╡
│ Adelie ┆ Torgersen ┆ 46.0 ┆ 21.5 ┆ 194.0 ┆ 4200.0 ┆ Male │
└─────────┴───────────┴────────────────┴───────────────┴───────────────────┴─────────────┴──────┘
flipper_length_mm 이상치:
shape: (2, 7)
┌─────────┬───────────┬────────────────┬───────────────┬───────────────────┬─────────────┬────────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═════════╪═══════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪════════╡
│ Adelie ┆ Biscoe ┆ 37.9 ┆ 18.6 ┆ 172.0 ┆ 3150.0 ┆ Female │
│ Adelie ┆ Torgersen ┆ 44.1 ┆ 18.0 ┆ 210.0 ┆ 4000.0 ┆ Male │
└─────────┴───────────┴────────────────┴───────────────┴───────────────────┴─────────────┴────────┘
body_mass_g 이상치:
shape: (2, 7)
┌───────────┬────────┬────────────────┬───────────────┬───────────────────┬─────────────┬────────┐
│ species ┆ island ┆ bill_length_mm ┆ bill_depth_mm ┆ flipper_length_mm ┆ body_mass_g ┆ sex │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═══════════╪════════╪════════════════╪═══════════════╪═══════════════════╪═════════════╪════════╡
│ Chinstrap ┆ Dream ┆ 52.0 ┆ 20.7 ┆ 210.0 ┆ 4800.0 ┆ Male │
│ Chinstrap ┆ Dream ┆ 46.9 ┆ 16.6 ┆ 192.0 ┆ 2700.0 ┆ Female │
└───────────┴────────┴────────────────┴───────────────┴───────────────────┴─────────────┴────────┘
# 그래프 크기 설정
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
features = ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']
feature_names = ['부리 길이', '부리 깊이', '날개 길이', '체중']
axes = [ax1, ax2, ax3, ax4]
for ax, feature, feature_name in zip(axes, features, feature_names):
# 종별로 박스플롯 생성
data = [penguins_df.filter(pl.col('species') == species)[feature].to_list()
for species in ['Adelie', 'Chinstrap', 'Gentoo']]
bp = ax.boxplot(data, labels=['Adelie', 'Chinstrap', 'Gentoo'],
patch_artist=True)
# 박스 색상 설정
colors = ['lightblue', 'lightgreen', 'lightcoral']
for patch, color in zip(bp['boxes'], colors):
patch.set_facecolor(color)
# 이상치 데이터 표시
if feature in outliers:
outlier_data = outliers[feature]
if len(outlier_data) > 0:
for i, species in enumerate(['Adelie', 'Chinstrap', 'Gentoo']):
species_outliers = outlier_data.filter(pl.col('species') == species)
if len(species_outliers) > 0:
ax.scatter([i + 1] * len(species_outliers),
species_outliers[feature],
color='red', marker='*', s=100, label='이상치')
# 이상치 레이블 추가
for row in species_outliers.iter_rows(named=True):
ax.text(i + 1, row[feature],
f"\n{row['sex']}\n{row['island']}",
ha='center', va='bottom')
ax.set_title(f'{feature_name} 분포와 이상치')
ax.grid(True, alpha=0.3)
plt.suptitle('펭귄 신체 특성별 이상치 분석', size=15, y=1.02)
plt.tight_layout()
plt.show()
# 그래프 크기 설정
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
features = ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']
feature_names = ['부리 길이', '부리 깊이', '날개 길이', '체중']
axes = [ax1, ax2, ax3, ax4]
for ax, feature, feature_name in zip(axes, features, feature_names):
# 종별로 박스플롯 생성
data = [penguins_df.filter(pl.col('species') == species)[feature].to_list()
for species in ['Adelie', 'Chinstrap', 'Gentoo']]
bp = ax.boxplot(data, labels=['Adelie', 'Chinstrap', 'Gentoo'],
patch_artist=True)
# 박스 색상 설정
colors = ['lightblue', 'lightgreen', 'lightcoral']
for patch, color in zip(bp['boxes'], colors):
patch.set_facecolor(color)
# 이상치 데이터 표시
if feature in outliers:
outlier_data = outliers[feature]
if len(outlier_data) > 0:
for i, species in enumerate(['Adelie', 'Chinstrap', 'Gentoo']):
species_outliers = outlier_data.filter(pl.col('species') == species)
if len(species_outliers) > 0:
ax.scatter([i + 1] * len(species_outliers),
species_outliers[feature],
color='red', marker='*', s=100, label='이상치')
# 이상치 레이블 추가
for row in species_outliers.iter_rows(named=True):
ax.text(i + 1, row[feature],
f"\n{row['sex']}\n{row['island']}",
ha='center', va='bottom')
ax.set_title(f'{feature_name} 분포와 이상치')
ax.grid(True, alpha=0.3)
plt.suptitle('펭귄 신체 특성별 이상치 분석', size=15, y=1.02)
plt.tight_layout()
plt.show()
12. 종합통계
summary_stats = {
'전체_개체수': penguins_df.shape[0],
'종별_개체수': penguins_df.group_by('species').count(),
'서식지별_개체수': penguins_df.group_by('island').count(),
'평균_체중': round(penguins_df['body_mass_g'].mean(),2),
'평균_날개길이': round(penguins_df['flipper_length_mm'].mean(),2),
'성비': penguins_df.group_by('sex').count(),
'가장_큰_종': penguins_df.group_by('species').agg(pl.col('body_mass_g').mean()).sort('body_mass_g', descending=True).head(1)
}
print("종합 통계:")
print(summary_stats)
summary_stats = {
'전체_개체수': penguins_df.shape[0],
'종별_개체수': penguins_df.group_by('species').count(),
'서식지별_개체수': penguins_df.group_by('island').count(),
'평균_체중': round(penguins_df['body_mass_g'].mean(),2),
'평균_날개길이': round(penguins_df['flipper_length_mm'].mean(),2),
'성비': penguins_df.group_by('sex').count(),
'가장_큰_종': penguins_df.group_by('species').agg(pl.col('body_mass_g').mean()).sort('body_mass_g', descending=True).head(1)
}
print("종합 통계:")
print(summary_stats)
종합 통계:
{'전체_개체수': 342, '종별_개체수': shape: (3, 2)
┌───────────┬───────┐
│ species ┆ count │
│ --- ┆ --- │
│ str ┆ u32 │
╞═══════════╪═══════╡
│ Adelie ┆ 151 │
│ Gentoo ┆ 123 │
│ Chinstrap ┆ 68 │
└───────────┴───────┘, '서식지별_개체수': shape: (3, 2)
┌───────────┬───────┐
│ island ┆ count │
│ --- ┆ --- │
│ str ┆ u32 │
╞═══════════╪═══════╡
│ Biscoe ┆ 167 │
│ Torgersen ┆ 51 │
│ Dream ┆ 124 │
└───────────┴───────┘, '평균_체중': 4201.75, '평균_날개길이': 200.92, '성비': shape: (2, 2)
┌────────┬───────┐
│ sex ┆ count │
│ --- ┆ --- │
│ str ┆ u32 │
╞════════╪═══════╡
│ Female ┆ 165 │
│ Male ┆ 177 │
└────────┴───────┘, '가장_큰_종': shape: (1, 2)
┌─────────┬─────────────┐
│ species ┆ body_mass_g │
│ --- ┆ --- │
│ str ┆ f64 │
╞═════════╪═════════════╡
│ Gentoo ┆ 5076.01626 │
└─────────┴─────────────┘}
종합 통계:
{'전체_개체수': 342, '종별_개체수': shape: (3, 2)
┌───────────┬───────┐
│ species ┆ count │
│ --- ┆ --- │
│ str ┆ u32 │
╞═══════════╪═══════╡
│ Adelie ┆ 151 │
│ Gentoo ┆ 123 │
│ Chinstrap ┆ 68 │
└───────────┴───────┘, '서식지별_개체수': shape: (3, 2)
┌───────────┬───────┐
│ island ┆ count │
│ --- ┆ --- │
│ str ┆ u32 │
╞═══════════╪═══════╡
│ Biscoe ┆ 167 │
│ Torgersen ┆ 51 │
│ Dream ┆ 124 │
└───────────┴───────┘, '평균_체중': 4201.75, '평균_날개길이': 200.92, '성비': shape: (2, 2)
┌────────┬───────┐
│ sex ┆ count │
│ --- ┆ --- │
│ str ┆ u32 │
╞════════╪═══════╡
│ Female ┆ 165 │
│ Male ┆ 177 │
└────────┴───────┘, '가장_큰_종': shape: (1, 2)
┌─────────┬─────────────┐
│ species ┆ body_mass_g │
│ --- ┆ --- │
│ str ┆ f64 │
╞═════════╪═════════════╡
│ Gentoo ┆ 5076.01626 │
└─────────┴─────────────┘}