Day20_SQL분석 33‐37_Python - bonniekwon0721/Dataanalytics-study GitHub Wiki

09/MAR/2024


active user 계산과 기간 단위 변경

  • User ID > 만들기 > 계산된 필드 > 이름: active user

    • COUNTD([User Id])
  • 열 선반에 ‘EVENT TIME’ 넣어주고 > 삼각형 클릭 > 더보기 > 사용자 지정

    • 세부 정보: 연도/월/일
  • 데이터패널 > 매개변수 만들기 > 이름: p_날짜단위

    • 데이터유형: 문자열
    • 허용 가능한 값: 목록
    표시 형식
    day 일(daily)
    week 주(weekly)
  • 매개변수 표시

  • 계산된 필드 만들기 > 이름: New Event Time

    • DATETRUNC([p_날짜단위], [Event Time], 'monday')
    • 열 선반에 만든 계산된 필드 ‘New Event Time’을 대체 적용하기.
    • 삼각형 클릭 > 더보기 > 사용자 지정

      • 세부 정보: 연도/월/일
  • 이제 아까 만들어 두었던 ‘매개변수 표시’에서 ‘주(weekly)’를 클릭하면 날짜 형식을 바꿀 수 있다.

이동 평균을 반영할 때의 고려사항

한 주의 이동평균 보기

  • 행 선반의 ‘집계(active user)’ > 퀵테이블 > ‘이동 평균’ 선택
  • 기존 ‘집계(active user)’을 행 선반에 추가로 올리고 ‘이중축’ 선택 > 그리고 ‘축 동기화’ 하기
    • 평균을 2 로 하느냐 3으로 하느냐에 따라서 ‘이동평균’은 변화됩니다.
    • 여기서 ‘테이블 계산 편집’ > 이전’6’으로 바꾼다면 요일별로 장기적 추세를 볼 수 있다.

한달을 4주로 보고 4주의 이동평균을 보고 싶다면?

  • 퀵테이블 만들기 > 이름: active user avg

    CASE [p_날짜단위] WHEN 'week' THEN WINDOW_AVG([active user], -3, 0) WHEN 'day' THEN WINDOW_AVG([active user], -6, 0) END

  • 해당 시트를 추가하여 아까 추가로 올렸던 ‘집계(active user)’를 제거하고 열 선반에 > New Event Time을 올리고 > 요일로 보기 선택

    • 추가로 분석에서 > 평균라인 > 패널 로 선택하여 그래프에 올리면
    • 각 요일의 평균을 볼 수 있음.

행동으로 전환된 고객의 비율

하루동안 방문한 전체 유저에 비해서 각각의 행동을 수행한 고객의 비율은 어떻게 구할 수 있을까?

1. 셋업

  • 행선반: Event Type
  • 열선반: active user
  • 표로 놓기

2. 계산된 필드 만들기

2-1) 먼저 cart 와 purchase간 고객 필터링

  • 이름: user id(cart) - IF [Event Type] = 'cart' THEN [User Id] END
  • 이름: user id(purchase) - IF [Event Type] = 'purchase' THEN [User Id] END

2-2) 그 다음 cart와 purchase고객 카운팅

  • 이름: active user(cart) - COUNTD([user id(cart)])
  • 이름: active user(purchase) - COUNTD([user id(purchase)])

2-3) 비율 구하기

  • 이름: conversion_cart - [active user(cart)] / [active user]
  • 이름: conversion_purchase - [active user(purchase)] / [active user]

3. conversion_cart 와 conversion_purchase 그래프 이중축 만들어보기

그리고 레이블 서식을 백분율로 나타내보자.

Appendix. 전환 고객 비율 간결하게 작성하기

위에서 2번 했던 계산을 한번에 나타낼 수 있다.

  • 계산된 필드 > 이름: conversion_cart_easy

    COUNTD(IF [Event Type] = 'cart' THEN [User Id] END)

    / COUNTD([User Id])

  • 계산된 필드 > 이름: conversion_purcase_easy

    / COUNTD([User Id])

    COUNTD(IF[Event Type] = 'purchase' THEN [User Id] END)

    / COUNTD([User Id])

거래액의 규모

거래액의 규모를 고객 행동별로 보자

1. 계산된 필드

  • 이름: price_purchase
    • SUM(IF[Event Type] = 'purchase' THEN [Price] END)

2. 행선반에 price_purchase / 열선반에 New Event Time 올리기

3. 행선반에 price_purchase 퀵테이블 > 비율 차이 그래프 하나 더 추가

  • 이중축 만들기

4. 축 편집 하여 범위 조절

  • 사용자 지정
  • 고정된 시작: 0 > 끝: 자동

5. 동일하게 cart도 계산된 필드 생성

  • 이름: price_cart
    • SUM(IF[Event Type] = 'cart' THEN [Price] END)

브랜드, 카테고리 테이블

1. 계산된 필드 만들기

  • 이름: count_prd
    • COUNTD([Product Id])
  • 이름: count_purchase
    • COUNT(IF [Event Type] = 'purchase' THEN [User Id] END)
  • 이름: count_view
    • COUNT(IF [Event Type] = 'view' THEN [User Id] END)
  • 이름: price_purchase
    • SUM(IF[Event Type] = 'purchase' THEN [Price] END)
  • 이름: purchase/view
    • SUM(IF[Event Type] = 'purchase' THEN [Price] END)

      / COUNT(IF [Event Type] = 'view' THEN [User Id] END)

2. 매개변수 만들기

  • 이름: p_브랜드/카테고리
    • 데이터 유형: 문자열
    • 허용 가능한 값: 목록
    • 값 Brand / 표시형식 브랜드
    • 값 Category id / 표시형식 카테고리

3. 계산된 필드 를 매개변수를 이용하여 만들기

  • 이름: 브랜드/카테고리

    CASE [p_브랜드/카테고리] WHEN 'Brand' THEN [Brand] WHEN 'Category Id' THEN [Category Id] END

  • 행선반의 값을 ‘브랜드/카테고리’로 변경

매개변수를 이용한 테이블 페이지 설정

1. 계산된 필드 만들기

  • 이름: INDEX
    • INDEX()
  • 해당필드는 불연속형으로 생성

2. 행선반 맨 앞에 넣어주기

3. 새로운 매개변수 생성

  • 이름: p_페이지
    • 데이터유형: 정수

    • 허용가능한 값: 범위 먼저 생성하고 목록으로 전환해주기

      • 최소값:20 / 최대값: 100 / 단위: 20
    • ‘목록’으로 허용 가능한 값 전환하고 나서 ‘표시 형식’ 살짝 만져주기

      1-20

      20-40 40-60 60-80 80-100

4. 위의 매개변수를 반영할 수 있는 계산된 필드 만들기

  • 이름: 페이지

    //index = 1 //20 //0 [p_페이지]>=[INDEX] AND [INDEX] > [p_페이지] - 20

5. 위의 ‘페이지’ 필드를 ‘필터’ 패널에 넣어주고 ‘참’ 선택

6. 정렬까지 마무리해주기

  • 브랜드 열에 > 필터 클릭
  • 정렬 기준: 필드
  • 정렬순서: 내림차순
  • 필드명 : price_purchase

쇼핑몰 헬스체크 대시보드 마무리

1. 동작 추가

  • 필터
    • 이름: 거래액>테이블
    • 원본시트: 거래액
      • 동작 실행 조건: 선택
    • 대상 시트: 브랜드/카테고리별 상세
      • 선택을 해제할 경우의 결과: 모든 값 표시
    • 필터: 선택한 필드
      • 원본 필드: YMD(New Event Time)

2. 동작이 작동되는 테이블 ‘브랜드/카테고리별 상세’ 테이블 이름 변경

  • <매개 변수.p_브랜드/카테고리>상세

3. 추가정보: 메뉴바 > 대시보드> 서식

  • 해당 대시보드의 모든 테이블의 서식이 바뀐다.

Chapter03

학습 개요 및 진행 방법

데이터 분석 Flow

  1. DB - DB적재, Relationship 이해, ERD
  2. SQL, Python - Adhoc Analysis, EDA, Data Transformation
  3. BI - Visualization, Reporting, Automation

데이터 분석 시나리오

분석 초기 세팅 데이터 탐구 대시보드 시각화
분석 환경 만들기 Python EDA BI Reporting
데이터셋 관계 이해 SQL Adhoc Query 데이터로 제안/설득하기
데이터 컬럼 특성 이해 가설과 인사이트 조직 Data literacy 제고

Google Colab을 활용한 Python EDA 개요

Exploratory Data Analysis : 탐색적 데이터 분석

  • Data 훑어보기 : with Pandas Data Frame
    • Info, describe
    • Null check
    • Duplicates check
    • Type check
  • Data 재구조화 : with Pandas Data Frame
    • Groupby
    • Picot_table
    • Stack/Unstack
  • Cols Relation 파악 : with Visualization
    • Heatmap Correlation
    • Bar / Regplot / Histplot
    • FacetGrid
    • Jointplot
    • 반복문으로 Subplots

데이터 빠르게 훑어보기 1 - info

데이터 가져오기

import pandas as pd
from google.colab import drive
drive.mount("/content/drive")
pd.read_csv("/content/drive/MyDrive/Colab_Notebooks/Zerobase/Class_SQL_Analystics/data/Chapter04/HR-Employee-Attrition.csv")

Data Quick Glance

  • Info()
  • describe()
  • NULL
  • dtype
  1. 열이 뭐뭐가 있니
df = pd.read_csv("/content/drive/MyDrive/Colab_Notebooks/Zerobase/Class_SQL_Analystics/data/Chapter04/HR-Employee-Attrition.csv")
df.columns 
  1. 열이 몇개나 있니
len(df.columns) 
  1. 테이블 정보좀 다 가져와봐
df.info()
# 중복값 여부 확인
print(len(df))
print(len(df.drop_duplicates())) # 중복된 값이 있으면 지워주고 보여줘

print(f"Duplicated Rows : {df.duplicated().sum()}") # 중복된 것이 있으면 세어줘

데이터 빠르게 훑어보기 2 - describe

df.describe() # 숫자 형식으로 된 값만 뽑아서 수치화해서 표현해주는 구문
df.describe().T # 숫자 형식으로 된 값만 뽑아서 수치화해서 표현해주는 구문
								# 컬럼과 로우를 바꿔서 보여줄래
df.describe().round(0).T # 숫자 형식으로 된 값만 뽑아서 수치화해서 표현해주는 구문
												# 컬럼과 로우를 바꿔서 보여줄래
												# 소숫점은 지워줄래

numpy 로 숫자 예시를 들겠음

import numpy as np

np.mean([1,2,3,4,100]) # 1부너 100까지 다 더한뒤 2로 나눈 값.

값은: 22.0 - 이상 수치의 영향을 많이 받았다.

np.median([1,2,3,4,100]) # 1부너 5까지의 중앙 값 # 중앙값은 이상 수치의 영향을 많이 받지 않는다.

값은: 3.0 - 중앙값은 이상 수치의 영향을 많이 받지 않는다.

pandas.DataFrame.describe

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.describe.html

describe가 숫자데이터 뿐만 아니라 문자 데이터까지 가져오려면?

: 하지만 문자형 데이터는 대부분 가져와서 describe 는 잘 안한다.

df.describe(include='all').round(0).T # 숫자 뿐 아니라 문자형의 형식도 포함한 값을 뽑아서 수치화해서 표현해주는 구문
# 컬럼과 로우를 바꿔서 보여줄래
# 소숫점은 지워줄래

데이터 세부사항 파악하기 - Data type

df.nunique() # 각 열당 유니크한 값이 몇개나 있니
# Data info
# 우리만의 테이블을 만들어서 값을 확인해보자. (열을 뽑아서)
pd.DataFrame({
    'Unique': df.nunique(),
    'Null' : df.isna().sum(),
    'Type' : df.dtypes
  }
)

DataFrame Row 순서 정렬: sort_values

pd.DataFrame().sort_values(by=”정렬 Column명”, ascending=TRUE or FALSE)
#오름차순은 ascending=TRUE
#내림차순은 ascending=FALSE
# Data info
# 우리만의 테이블을 만들어서 값을 확인해보자. (열을 뽑아서)
pd.DataFrame({
    'Unique': df.nunique(),
    'Null' : df.isna().sum(),
    'Type' : df.dtypes
  }
).sort_values(by="Unique", ascending=False)

유니크한 값이 1개인 열은 드랍을 하겠습니다.

DataFrame 일부 Data Drop(삭제) 하기

pd.DataFrame().drop([Col1,Col2,...]
										, axis = 1
										, inplace=True)
  • 상세 설명

    Inplace 파라미터는 DataFrame 에 생긴 변동사항을 원본 DataFrame 에 최종 반영할 것인지의 여부를 정하는 것으로, False가 디폴트

    • TRUE → 해당 DataFrame 된 결과물이 저장 반영되는 것
    • FALSE → DataFrame 변수 자체에는 저장되지 않으나, 결과물 조회만 하는 것
df.drop(['StandardHours','Over18','EmployeeCount'],axis=1,inplace=True)
df

열 지워졌는지 확인

# 열의 갯수를 확인하면 지워졌는지 알 수 있다.
len(df.columns)

특정 데이터 타입만 보고싶어요

# int / Object 이 섞여 있음. - object타입의 열만 보고싶어요.
df.select_dtypes(include='object')
# int / Object 이 섞여 있음. - int64타입의 열만 보고싶어요.
df.select_dtypes(include='int64')

사내 조직 파악하기 1 - groupby

**-pandas DataFrame**
- groupby
- pivot_table
# 조직을 파악하기 위한 열을 가져오겠음.
tmp0 = df[['Department','EducationField','JobRole','JobLevel','Attrition']].copy()
#copy 를 해준다는 것은 원본 데이터프레임에 영향가지 않게 하기 위함.

tmp0.groupby('Department').size()
tmp0.groupby('Department')['Department'].count()
#앞에 열 이름은 groupby 로 묶을 열 이름.
#뒤의 열 이름은 어떤 대상을 집계를 할지
#맨뒤는 aggregates (집계)
tmp0.groupby('Department')['Department'].agg('count')
  • 위의 세개 구문은 다 같은 값을 가져온다. size() / count() / agg(’count’)

사내 조직 파악하기 2 - pivot_table

tmp0.groupby(['Department','JobRole','JobLevel','EducationField']).size().sort_values(ascending=False).reset_index(name='Emp Count')
  • 상세 설명

    이 코드는 tmp0이라는 데이터프레임을 여러 열('Department', 'JobRole', 'JobLevel', 'EducationField')에 따라 그룹화하고, 각 그룹에 속하는 행의 수를 계산한 후, 이 결과를 내림차순으로 정렬합니다. 그 다음, 이 결과를 새로운 데이터프레임으로 만들고, 행의 수를 나타내는 열을 'Emp Count'라고 이름지어 재설정합니다.

    코드의 구성을 분석해보겠습니다:

    • groupby(['Department','JobRole','JobLevel','EducationField']): 이 부분은 데이터를 'Department', 'JobRole', 'JobLevel', 'EducationField' 열에 따라 그룹화합니다. 이는 분석에서 특정 부서, 직무, 직급, 교육 분야별로 데이터를 나누고 싶을 때 유용합니다.
    • size(): 각 그룹에 속하는 행의 수를 계산합니다. 이는 그룹별 직원 수를 알고 싶을 때 유용합니다.
    • sort_values(ascending=False): 계산된 행의 수를 기준으로 데이터를 내림차순으로 정렬합니다. 이렇게 하면 가장 많은 직원이 있는 그룹부터 순서대로 볼 수 있습니다.
    • reset_index(name='Emp Count'): 이 부분은 결과를 새로운 데이터프레임으로 만들고, 인덱스를 리셋합니다. 'name='Emp Count''는 그룹별 크기를 나타내는 열의 이름을 'Emp Count'로 설정합니다.

    이 코드는 데이터를 면밀히 분석하고 싶을 때 매우 유용합니다. 예를 들어, 회사의 다양한 부서에서 특정 직무, 직급, 교육 분야별로 얼마나 많은 직원이 있는지 알고 싶을 때 이 코드를 사용할 수 있습니다.

    비효율적인 부분을 개선할 수 있는지 여부는 데이터의 크기와 구조, 그리고 분석의 목적에 따라 다릅니다. 이 코드는 이미 상당히 최적화된 형태로 보입니다. 하지만, 매우 큰 데이터셋을 다룰 때는 groupby 연산이 메모리를 많이 사용할 수 있으므로, 데이터를 처리하기 전에 필요하지 않은 열을 제거하는 등의 방법으로 데이터의 크기를 줄이는 것이 좋습니다.

Groupby 로 다중의 집계함수를 사용 가능합니다.

tmp1 = df[['Department','JobRole','JobLevel','Age','Attrition']].copy()
tmp1.groupby(['Department','JobRole'])['Age'].agg(['count','max','min'])
#groupby 로 다중의 집계함수를 사용 가능합니다.

Pivot table

pt1 = pd.pivot_table(tmp1,
               index=['Attrition','Department'],
               columns='JobLevel',
               values='Age',
               aggfunc='count',
               fill_value='')
pt1

사내 조직 파악하기 3 - unstack, stack

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.unstack.html

Unstack 연습을 위한 Pivot Table

pt2 = pd.pivot_table(tmp1,
               index=['Attrition','Department','JobRole'],
               columns='JobLevel',
               values='Age',
               aggfunc='count',
               fill_value='')
pt2
pt2.index

Unstack 연습

# 0: 'Attrition', 1: 'Department', 2: 'JobRole'
pt2.index.names
# unstack은 컬럼쪽에 아래로 길게 있던걸 로우로 우측으로 길게 배열해주는 것.
# stack 은 반대로 로우로 우측으로 길게 있던걸 컬럼쪽에 아래로 길게 배열해주는 것.
pt2.unstack(level=0).stack(level=0)
st1 = pt2.unstack(level=0).stack(level=0)

st1.index

  • 상세 설명

    unstack()stack() 함수는 Pandas 데이터프레임의 구조를 변환하는 데 사용되며, 멀티레벨 인덱스(multi-level index) 또는 컬럼에 대해 작동합니다. 이 함수들은 데이터의 차원을 재구조화하는데 유용하게 사용됩니다.

    • unstack() 함수는 데이터프레임의 로우(행)를 컬럼(열)으로 "펼치는" 작업을 수행합니다. 즉, 데이터프레임이나 시리즈에서 멀티레벨 인덱스의 특정 레벨을 컬럼의 방향으로 회전시켜 재배치합니다. 이는 데이터를 넓게(wide format) 만들고 싶을 때 유용합니다.
    • stack() 함수는 unstack()의 반대 작업을 수행합니다. 즉, 컬럼을 로우 방향으로 "쌓아서" 데이터프레임의 구조를 길게(long format) 만듭니다.

    pt2.unstack(level=0).stack(level=0) 코드는 두 가지 작업을 연속으로 수행합니다:

    1. 먼저, unstack(level=0)을 사용하여 데이터프레임 pt2에서 가장 바깥쪽 레벨(0 레벨)의 인덱스를 컬럼으로 펼칩니다. 이로 인해 데이터 구조가 넓게 변형됩니다.
    2. 그 다음, stack(level=0)을 통해 방금 컬럼으로 변형된 구조를 다시 로우 방향으로 쌓아서 원래의 레벨로 돌려놓습니다.

    이 과정을 거치면, 원본 pt2 데이터프레임의 구조가 일시적으로 변형되었다가 다시 원래의 구조로 복귀합니다. 그러나, 이 과정을 통해 인덱스나 컬럼의 순서가 변경될 수 있으며, 데이터의 구조를 보다 명확히 이해하거나 특정 분석 목적에 맞게 조정하는데 도움을 줄 수 있습니다.

    이러한 작업은 데이터를 다양한 방식으로 재구조화하고자 할 때 유용하게 활용될 수 있으며, 특히 멀티레벨 인덱스를 가진 복잡한 데이터프레임을 다룰 때 매우 유용합니다.

Appendix. matplotlib과 seaborn

matplotlib seaborn
https://matplotlib.org/stable/plot_types/index https://seaborn.pydata.org/examples/index.html
Python 대표 시각화 Library Matplotlib 기반의 Adds-on 성격의 Library
기본적 그래프부터 통계, Image 처리까지 Documentation 과 Cheatsheet 참고! 간단한 메서드로 다양한 통계 그래픽

Matplotlib과 Seaborn을 활용한 시각화 - Heatmap (1)

# numeric데이터만 걸러서 상관관계를 분석하는 구문 
df.corr()
# numeric데이터만 내가 직접 걸러서(int64) 상관관계를 분석하는 구문 
df.select_dtypes(include='int64').corr()
import matplotlib.pyplot as plt
import seaborn as sns

cor_df = df.select_dtypes(include='int64').corr()

# 이렇게만 해도 heatmap은 나오나 알아보기 어렵다.
sns.heatmap(cor_df.corr())
# 사이즈를 넣어주고, 수치를 기입했어요.
plt.figure(figsize=(15,10))
sns.heatmap(cor_df.corr(),
            annot=True)
plt.show()

Heatmap Parameters → 그래프 꾸며주기

https://seaborn.pydata.org/generated/seaborn.color_palette.html

seaborn.heatmap(date
								,annot=True    #Heatmap에 숫자 표기
								,fmt='.1f'.    #Annotation Formatting - 소수점 표기
								,linewidth=0.5 #Heatmap 사이 Line 두께
								,cmap='Blues | YlGnBu | RdYlBu_r' #색상 Color map
#heatmap 꾸미기
plt.figure(figsize=(15,10))
sns.heatmap(cor_df.corr(),
            annot=True,
            fmt='.1f',
            linewidth=0.5,
            cmap='RdYlBu_r')
plt.show()
#heatmap 꾸미기
plt.figure(figsize=(15,10))
sns.heatmap(cor_df.corr(),
            annot=True,
            fmt='.1f',
            linewidth=0.5,
            cmap='YlGnBu')
            #vmin=-1,vmax=1) #강제로 초대와 최소를 지정해주는 기능
plt.show()
#heatmap 꾸미기
plt.figure(figsize=(15,10))
sns.heatmap(cor_df.corr(),
            annot=True,
            fmt='.1f',
            linewidth=0.5,
            cmap=sns.color_palette("Spectral", as_cmap=True))
plt.show()

데이터 상관관계 한 눈에 파악하기 1 - Heatmap (2) 🤩

좀 더 상관관계가 높은 열만 가져와서 히트맵을 그리자.

# 정수형 데이터만 가져와서 값을 합하자. 
# 합을 하여 상관관계가 높을수록 숫자가 높다고 보면 됨.
df.select_dtypes(include='int64').corr().sum().sort_values(ascending=False)[:8]
# 상관관계 높은 8개 애들만 리스트를 가져오세요
top8_cols = df.select_dtypes(include='int64').corr().sum().sort_values(ascending=False)[:8].index.tolist()
top8_cols
cor_df2 = df[top8_cols].corr()
sns.heatmap(cor_df2, annot=True, fmt='.1f', cmap='Blues')
# 삼각형 Mask 씌워서 Heatmap 깔끔하게 그리기
import numpy as np

mask = np.zeros_like(cor_df2, dtype=bool)
mask[np.triu_indices_from(mask)] = True
sns.heatmap(cor_df2, annot=True, fmt='.1f', cmap='Blues', mask=mask)

시각화를 통한 데이터 드릴다운 1 - Bar

# Simple version - Matplotlib
plt.bar(df['JobLevel'], df['MonthlyIncome']) #plt.bar(x, y)
plt.show()
# 조금 더 이쁘게 꾸며주기
plt.title('Simple Bar plot - Income by JobLevel')
plt.bar(df['JobLevel'], df['MonthlyIncome']) #plt.bar(x, y)
plt.xlabel('JobLevel')
plt.ylabel('MonthlyIncome')
plt.show()
# 다른 그래프도 그려보자
plt.title('Simple Bar plot - TotalWorkingYears by JobLevel')
plt.bar(df['TotalWorkingYears'], df['MonthlyIncome']) #plt.bar(x, y)
plt.xlabel('TotalWorkingYears')
plt.ylabel('MonthlyIncome')
plt.show()

시각화를 통한 데이터 드릴다운 2 - FacetGrid

위의 그래프를 드릴 다운에서 좀 더 세분화해서 보고싶을 때 FacetGrid 를 사용합니다.

https://seaborn.pydata.org/generated/seaborn.FacetGrid.html

# 부서별로 보면 좀 다르지 않을까?

facet = sns.FacetGrid(df, col='Department')
facet.map_dataframe(sns.barplot, x='TotalWorkingYears', y='MonthlyIncome')
# 부서별로 보면 좀 다르지 않을까?

facet = sns.FacetGrid(df, col='Department', height=6)
facet.map_dataframe(sns.barplot, x='TotalWorkingYears', y='MonthlyIncome')

# 조금 보기 좋게 합시다.
facet = facet.fig.subplots_adjust(wspace=.4, hspace=.2)
# 부서별로 보면 좀 다르지 않을까?

facet = sns.FacetGrid(df, col='Department', row='JobLevel', height=6) # 조금더 드릴다운 해서 row='JobLevel'까지 봅시다.
facet.map_dataframe(sns.barplot, x='TotalWorkingYears', y='MonthlyIncome')

# 조금 보기 좋게 합시다.
facet = facet.fig.subplots_adjust(wspace=.4, hspace=.2)

요것은 다른 주제.

facet = sns.FacetGrid(df, col='JobLevel', row='Gender', height=3) 
facet.map_dataframe(sns.barplot, x='TotalWorkingYears', y='MonthlyIncome')
plt.show()
facet = sns.FacetGrid(df, col='JobLevel', row='Gender', height=3) 
facet.map_dataframe(sns.barplot, x='YearsAtCompany', y='MonthlyIncome')
plt.show()

Studied from 제로베이스 데이터스쿨

⚠️ **GitHub.com Fallback** ⚠️