WeniVooks

검색

Right Now, Polars

펭귄 데이터 분석

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)
(344, 7)
(344, 7)

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  │
└─────────┴─────────────┘}
{"packages":["numpy","pandas","matplotlib","lxml"]}
4.6 연비 데이터 분석