필요 라이브러리 import

%matplotlib nbagg
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


파일 읽기 + 헤더를 새로운 것으로 지정해주기(기존 csv데이터와 다른이름의 헤더 주기)

pd.read_csv를 이용하여, (notebook작성위치 기준)파일을 불러오면서, 헤더옵션을 주어 새로운 헤더를 지정해주자.(갯수는 동일하게)

names = pd.read_csv("data/us-baby-names/NationalNames.csv", sep="," ,
                     header=0, names=["id", "name", "year", "sex", "births"])

image


  • 데이터 확인하기 ( head()와, shape 관찰하기)


데이터 분석1 – 각 연도별, 성별(남/여)의 총 출생횟수 계산하기 –> 시각화  by 피벗테이블

각 연도별-> 기준열1 “year”
성별(남/여)의 –> 기준열2 “sex”
총 출생횟수 –> 추출열 + sum()통계함수 “births” / “sum

  • groupby()를 이용한다면, names.groupby( [ 기준열1, 기준열2 ] ) [ 추출열 ] .sum()이겠지만
    피벗테이블을 이용해서 데이터그룹화해보자.
    totol_births = names.pivot_table(values="births", index="year", columns="sex", aggfunc=sum)
    totol_births.head()
    image


  • line plot을 그려보자.( index-> x축 / values의 범위 –> y축 /  columns –> 선의 갯수 = 범례의 갯수)
    ax = total_births.plot()
    image
  • plot의 제목을 붙혀주자.
    ax.set_title("Total births by year and sex")
    image



데이터 분석2 – 각 (연도, 성별 기준의) 그룹내에서,
                  각 이름(행)의 출생횟수가 전체에서 차지하는 비중(%)을 나타내는 열 추가하기 by groubpy()함수

피벗테이블을 사용하지 않고, groupby()함수를 사용해서 계산을 해보자.

앞서 22강에서는, 기준열에 대한 데이터그룹화 직후의 결과물( 추출열 적용x, 통계함수적용x)을 보기 위해서는
데이터그룹화 결과물에 list()를 입혀서  [ (기준열성분a, 기준열에a를 가진 dataFrame ) ,  ( b : DataFrameB) , ( c : dfC )] 형태로 나누어지고,
그 list에다가 dict()를 입혀서 { a : a포함 df , b : b포함 df, c: c포함df }의 딕셔너리 형태로 가져와서 볼 수 있었다.
그리고 그 딕셔너리에 [키인덱싱]을 통해서, 해당 키성분을 가진 df를 추출할 수 있었다.


여기서는 2개의 기준열을 가지고 groupby하여 딕셔너리 형태로 뽑아보자. 키가 2개가 될 것이고, 키 2개 성분을 가지는 df가 밸류가 될 것이다.

  • year와 sex열을 기준열로 하여,  groupby함수의 그룹화결과물을 딕셔너리형태로 담아보자.
    이 때, 딕셔너리이므로, head()가 아니라 keys()를 통해,   기준열2개-> 2개의 key ( year, sex)형태의 키들을 살펴볼 수 있다.
    grouped_names_dict = dict(  list(  names.groupby( ["year", "sex"])  )  )
    grouped_names_dict.keys()

    image

  • 기준열 2개로 groupby()한 그룹화결과물을 ---> 딕셔너리로 저장했다면, 보고싶은 2개의 키를
    딕셔너리에 [키 인덱싱]하는데, (key1, key2 ) 순으로 넣어주면 해당 dataFrame을 볼 수 있다.
    예를 들어, 2011년도 남자(M)아이에 해당하는 names데이터프레임만 보고 싶다면,
    해당 딕셔너리의 (2011, ‘M’) 키를 가진 –> 밸류(df)만 가져오면 된다.
    추출열 없이, 전체 names중에 기준을 가지는 행들의 모든 열이 나타나기 때문에,  아이의 name과 출생횟수도 바로 볼 수 있다.

    grouped_sample = grouped_names_dict[ (2011, "M" )]
    grouped_sample.head(10)
    image
    my) dataFrame을 groupby()를 통해서, 딕셔너리형태로 그룹화결과물만 가지고 있다면,
    names라는 df 중, 딕셔너리에서 키값만 인덱싱하면, 원하는 기준으로 자료를 뽑아낼 수 있다.
    굳이 통계함수를 적용안시켜도되며 & 기준열중 원하는 값만을 가지는 것을 바로 뽑아낼 수 있다.
    추출열 없이, 전체 names중에 기준을 가지는 행들의 모든 열이 나타나기 때문에, 
    다른 열의 정보인 아이의 name과 출생횟수도 바로 볼 수 있다.
    (나는 이 딕셔너리를 키값인덱싱으로 개별 결과물을 알 수 있는 그룹화 기준 딕셔너리 라고 부를 것이다.
    이 그룹화 기준 딕셔너리에 키값으로 인덱싱하여 –> 각각의 그룹화된 df을 얻을 것이다)

    참고로,, 그룹화기준 딕셔너리 생성 –> 키값인덱싱으로 그룹화된 결과물 1개를 살펴보기 위함 일뿐?
    결과물 1개를 봐야지.. 집계함수를 정의할 수 있어서?

  • 이제 1개의 그룹화결과물 df에 대해 출생비율을 계산하여 새로운열로 추가해줄 사용자 정의 함수를 정의한다.
    기준열에 2개에 대한 기준값을 –> 그룹화 기준 딕셔너리에 키값 2개( year, ‘M’ or ‘F’) 를 인덱싱하여하여 얻을 수 있는 DataFrame을 인자로 받아
    해당 추출된 df의 births열을 따로 빼낸 뒤 –>
    [ births열 변수  / births열 변수 .sum()총합 ]의 비율계산하여 –>
    새로운 “prop”열을 df에 추가하는 함수다.

    def add_prop(agg_df):
         agg_births = agg_df["births"]
         agg_df["prop"] = agg_births / agg_births.sum()
         return agg_df
    image


  • 이제 사용자 정의 집계함수를 적용시키려면, 그룹화기준 딕셔너리에 키값을 입력해서 얻는 1개의 그룹화 결과물이 아니라,
    groubpy로 그룹화시킨 것에 추출열이 없는 전체 dataFrame에 .apply()를 이용해서 사용자 정의함수를 적용한다.

    names_with_prop = names.groupby( ["year","sex"]).apply(add_prop)
    names_with_prop.head()
    image
    pivot_table기준열을 index / columns로 가져와서 보기가 편해던 반면에,
    groupby는 기본적으로, 열로 유지된 상태의 데이터라 보기 쉽지 않다.
    하지만, [ 그룹화기준딕셔너리로 1개의 결과물 본 뒤-> 각 그룹화 결과물을 전체로 한 비율을 계산해줄 함수정의 –> 함수 적용으로 새로운 열로 추가 ]가 가능하다.
    ( my) 각 그룹화결과물을 전체로 하였으니,, 각 비율은 그룹화했을 때, 합이 1이다. 전체데이터에서 prop열을 보면, 합이 1이 아니고, 섞여있다)


데이터 분석3 – 각 연도, 성별 그룹내에서,  출생횟수(births) 기준 TOP1000 이름 추출하기 by groupby()

상위 TOP 1000 등을 계산하려면, 데이터분석2에서 계산한 prop(비율)열을 먼저 계산되어 있어야한다.?????
위에서 return받은 데이터 names_with_prop을 이용하자.
image

  • 각 그룹화결과물을 전체로한 비율을 열로 추가한 데이터 names_with_prop을 가지고, 또 [ 그룹화기준 딕셔너리 ]를 만들자.
    < 결국에는 groupby로 그룹화한 데이터를 다룰 것이다. 그 전에  딕[키값인데싱]으로 1개의 결과물을 미리 볼 수 있다 >
    grouped_names_with_prop_dict = dict( list( names_with_prop.groupby(["year", "sex"]) ) )

    하나의 샘플을 미리 보자.
    grouped_sample = grouped_names_with_prop_dict[(2011, "M")]
    grouped_sample.head()
    image


  • 이제 우리는, 그룹화기준 딕셔너리를 통해 1개의 그룹 결과물을 보고서 –> 각 그룹 별 상위 1000개의 데이터를 추출해야한다.
    1개의 그룹화 결과물에 대해서births열로 내림차순으로 정렬하고,  .iloc[ :10]을 통해,  10번째 행까지만 뽑아보자!

    grouped_sample.sort_values(by="births", ascending = False).iloc[:10]
    image
    (기존의 데이터가 원래 내림차순으로 정렬되어있던 데이터라서,, 동일한 것처럼 보인다)

  • 위의 1개 결과물에 대한 것에 착안을 해서 ---> 그룹화기준딕셔너리를 만들 때 쓴, 전체 데이터 names_with_prop에 대해서
    1개의 결과물에 적용한 과정을 그대로의 수행하는 사용자 정의함수를 정의하자.
    (1) births열 내림차순정렬 + 1000번째 행까지만 인덱싱하여—> top1000_df 변수에 저장
    (2) top1000_df변수를 return함

    def get_top1000(agg_df) :
         top1000_df = agg_df.sort_values(by="births", ascending = False ).iloc[:1000]
         return top1000_df
    image


  • 정의한 함수를 groupby()한 그룹화결과물에 (통계함수대신) .apply()로 적용한다.
    각 연도, 성별을 기준으로 그룹화된 각 DataFrame에서 births 상위 1000명개씩 끊어서 name행들이 추출될 것이다.

    top1000_names = names_with_prop.groupby(["year", "sex"]).apply(get_top1000)
    top1000_names.head()
    image
    다 보이진 않지만, 1880년도, Female 아이들 중 상위1000개 나타나고 그다음, Male의 상위1000개만 나타날 것이다.


  • 확인을 위해, 전체 데이터인 names_with_prop 의 행인덱싱.loc[]의  조건으로 ( year열 == 2011) & ( sex열 == “M” )을 주어,
    2011년에 태어난 M인 아이들의 수
    shape를 통해 총 몇 행이 있는지 확인하고
    각 그룹결과물 별 top1000데이터 top1000_names의 행인덱싱에 조건을 주어 shape를 확인해보자.
    (.loc[ 인덱스명 ,  칼럼명 ]으로 인덱싱하는것이 보통이나, 행인덱싱란에  df[“특정열”] ==특정성분 형태로 조건을 넣을 수도 있다)
    names_with_prop.loc[ (names_with_prop["year"] == 2011) & ( names_with_prop["sex"] == "M")].shape

    top1000_names.loc[ (top1000_names["year"] == 2011) & (top1000_names["sex"] == "M" )].shape

    image
    사용자정의함수를 이용해서, 각 그룹화결과물에 대해, births열을 내림차순후, 상위1000개만 뽑은 것을 확인할 수 있다.



(데이터분석3에 이은 것)

데이터분석 4 – 각 연도에 따른, 성별의 전체 출생횟수 대비 TOP 1000이름들의 출생횟수 비중의 합 산출하기 by 피벗테이블

각 연도별 각 성별의, 전체 출생횟수 대비, 상위 top1000개의 출생횟수 비중이, 어떻게 변화되었는지 알아보자.
이전에, 각 (연도, 성별)별 그룹의 전체 출생횟수 대비, 각 이름들의 출생횟수 비중을 의미하는 “prop”열을 만들어놨었다.
이 것을 이용해 pivot_table을 만들면 굉장히 편할 것이다.

먼저 top1000_names.head() 를 통해 데이터를 다시한번 살펴보자.  각 연도별/성별별 그룹마다 상위1000개의 행들만 나타나있다.
image


  • 먼저, top1000_names에서, pivot_table을 이용하여, 
    prop열을 추출열로 하고,
    year열을 index로 가져오는 기준열1
    sex열을 columns로 가져오는 기준열2
    통계함수는 sum을 적용한다.
    연도별 / 성별별 / 비중들의 합이 되므로, 이전에 각 그룹별로 전체 비중을 계산했으니, 각각 1으로 찍혀야하나,
    상위 출생횟수 1000개씩 골라냈으므로, 1이되거나 1이 좀 못될 것이다.
    즉, [ 각 연도/성별별 그룹마다 전체대비 비중계산prop 추가 –> 각 그룹별 TOP 1000개씩 인덱싱 –>
          각 연도/성별별 그룹의 상위1000개, prop열 추출 = 자동으로 전체대비 상위1000개차지하는 비중의 합을 계산 ]
    빨간색은 데이터분석2번 –> 파란색은 데이터분석3번 –> 보라색은 지금 한 것이다.

    top1000_props = top1000_names.pivot_table(values="prop", index="year", columns="sex", aggfunc="sum")
    top1000_props.head(10)
    image

  • 이제 각 연도별/성별별/ 전체출생횟수대비, 상위1000개의 출생횟수비중의합을 라인플롯으로 그려보고 수정해주자.

    ax = top1000_props.plot() 을 통해 그리고
    ax.set_xticks( range(1880, 2020, 10))    ---------> 파이썬의 range정수 리스트 생성
    ax.set_yticks( np.arange(0, 1.3, 0.1)) 을 통해서,  ---> 넘파이의 np.arangearray리스트로 실수/ 행렬까지 가능
    x축은 1880년부터 2020년 전까지, 10의 간격으로 / y축은 0부터 1.3전까지 0.1간격으로 주자.

    image
    간단히 해석해보면)
    1880년도 같은 경우, 비중이 1이다.  1880년에 태어난 아이들의 이름은,, 거의 상위1000개 아름안에 다 들어간다.
    그러나 시간이 흐를 수록, 상위1000개의 이름의 비중이 줄어든다.
    즉, 시간이 지남에 따라 인기는 이름의 비중은 내려가고, 이름에 개성이 생기고 있다.



데이터 분석 5 – 각 연도에 따른, 특정 이름들의 , 출생횟수 변화 추이 분석하기 by 피벗테이블

top1000개 중 몇개의 이름들만 대상으로 하여, 연도에 따른 출생횟수 변화추이를 분석하자.
기존의 top1000_names를 그대로 이용할 것이다. (각 연도별/성별별/ 탑1000개의 이름데이터들이 나와있다)
top1000_names.head()
image
피벗테이블을 활용할 것이므로, 해석을 하자면
각 연도에 따른 –> 기준열1 (index)
특정 이름들의 –> 기준열2(columns)  + 특정이름이라는 것은,,  비 인기이름들은 연도별로,, 출생횟수가 아예 없는 것도 있으니 NaN으로 뜬다
                                                                           그것들 베재하고 인기있는 것들만 살펴보자.
출생횟수 –> 추출열(values)
변화 –> 통계함수(aggfunc)는 sum으로해서,, 이름별,, 출생횟수의 합으로 변화를 살펴야한다

  • top_names_births = top1000_names.pivot_table(values="births", index="year", columns="name", aggfunc="sum")
    top_names_births.head()
    image
    이름이 7031가지나 되고 그것이 columns으로 나타난다


  • 위의 7031가지 이름들 중에, 열인덱싱으로, 특별한 이름 4개만 가져와보자.
    top_names_births_subset = top_names_births[ ["John", "Harry", "Mary", "Marilyn"] ]
    top_names_births_subset.head()
    image


  • top_names_births_subset 을 가지고 라인플롯을 그려보자.
    이 때, plot()의 인자로, subplots =True를 주게 되면, 하나의 ax(axes좌표평면)에 각 칼럼들=각 라인들이 subplot안에 독립되어 그려진다.

    ax = top_names_births_subset.plot( subplots = True , fontsize = 8)
    image
    간단한 해석 )  나머지이름들은 20세기 전체적으로 인기가 있었는데 반면, Marilyn은 20세기 중반에 반짝 인기 있다가 사라졌다.



데이터분석 6 – 남아의 이름 마지막글자에 따른 출생횟수가, 연도에 따라, 어떻게 변화하였는지 분석하기 by 람다/피벗테이블

저명한 연구자에 따르면, 지난 100년동안 남아아이름의 마지막글자의 분포가 급격하게 변화하였다고 알려왔다. 그것을 조사해보자.
처음 사용한 데이터 names에다가,  람다함수를 정의한 뒤, 적용해볼 것이다.
image

  • 먼저 람다함수를 하나 정의해줄 것이다.
    어떤 시리즈(열1개)에 대하여, 각 성분(x)의 마지막 글자( x[-1])를 추출하여 반환하는 함수다.
    get_last_letter = lambda x: x[-1]

  • names의 name열 에다가 .apply()를 통해 람다함수를 적용하게 되면, 각 이름의 마지막 글자만 추출된 열(시리즈)이 생성될 것이다.

    names["name"].apply(get_last_letter).head()
    image

    이것을 names의 새로운 열로 추가해주자.
    names["last_letters"]= names["name"].apply(get_last_letter)
    names.head()
    image


  • (람다함수로 마지막글자 추출후반환 정의 –> name열에 적용-> 새로운 열로 추가)의 과정을 거쳐
    이제 name의 마지막글자를 새로운 열로 추가한 names데이터를 가지고  피벗테이블사용할 것이다.
    < groupby는 그룹화기준 딕셔너리를 통해, 1개의 그룹화결과물을 보고, 사용자정의 def함수를 적용시킬 때 사용했고
      pivot_table은 2개의 기준열을 index, columns로 가져오면서, 통계함수를 빠르게 적용시킬 때 사용했었다.>

    남아이름 마지막글자에 따른 출생횟수가, 연도에 따라, 어떻게 변화(총합)하였는지 분석 이므로
    추출열 = 출생횟수 / index 기준열1 = 마지막글자열 /  columns 기준열2 = 남아(성별) - 기준열3 = 연도(계층적컬럼)/ 통계함수 = sum

    names.pivot_table(values = "births", index ="last_letters", columns = ["sex", "year"] , aggfunc = "sum")
    image


  • 남아에 대한 정보만 필요하므로 크게 잘라내기 위해, 성별을 연도보다 컬럼의 상위계층으로 두었었다.
    (나중에 남자 / 여자 따로 plot으로 나타낼 것이다. )
    위 피벗테이블을 새로운 변수 last_letters_table에 먼저 담아둔다.
    last_letters_table = names.pivot_table(values = "births", index ="last_letters", columns = ["sex", "year"] , aggfunc = "sum")

  • 연도가 너무 많으므로 df.reindex()함수를 사용해서 특정컬럼만 골라낼 수 있다. 이 때 계층적 컬럼이므로 level인자도 같이주어야한다
    (13강에서는 reindex(columns = [  , , ]를 통해, 기존 있던 칼럼들은 다 기입하되, 순서만 다르게 주어서, 칼럼 순서바꾸기로 쓰였었다.)

    three_years_subtable = last_letters_table.reindex(columns=[1910, 1960, 2010] , level ="year")
    three_years_subtable
    image


  • 이 때, three_years_subtable에 .sum()함수를 적용하면 기본적으로 가장 하위층의 각 열마다 총합을 구해준다.
    즉, 성별에 따라 / 각 연도별 / 마지막글자 a부터~z까지의 총 출생횟수의 합이 나온다.
    이것을 구하는 이유는, a~z까지 전체 출생횟수에 대한 각 a, b, c 들의 비중을 구하기 위해서다.
    three_years_subtable.sum()
    image

    이제 전체에 대한 비중을 구하는 식인 three_years_subtable / three_years_subtable.sum()를 계산하여
     비중 dataframe을 따로 변수에 저장한다.

    three_years_letters_prop = three_years_subtable / three_years_subtable.sum()
    three_years_letters_prop.head()
    image
    결과적으로 얻은 결과물은,
    각 성별 / 연도에 따른, 마지막글자를 a~z로 가진 아이의 출생횟수의 상대적 비중을 산출한 것이다.


  • 이번에는 ax = df.plot()으로 바로 안그리고, figure를 생성하면서 plot을 그려보자.
    figure와 axes를 만들고, figure안에 subplot을  2x1의 2개로 만든다.

    fig, axes = plt.subplots( 2, 1)
    image
    이 때, 한 fig에 대해서 subplot이 2개면 –> axes1차원 리스트의 형태로 2개가 반환된다. axes[0]과 axes[1]

  • 첫번 째 axes인  axes[0]을 plot()함수의 ax인자로 주어 그리되,
    열인덱싱으로 M(남아)만 / 바플롯 / 첫번째 axes / title도 동시에 준다.
    (계층적 인덱스라도, 가장 상위계층인 M/ F열은 바로 인덱싱하면된다 ㅋ)

    three_years_letters_prop["M"].plot(kind="bar", ax=axes[0], title="Male")
    image


    F열에 대해서도 똑같이 그리되, 2번째 좌표평면에다가 / title을 바꿔서 그려준다.

    three_years_letters_prop["F"].plot(kind="bar", ax=axes[1], title="Female")
    image

    글자가 겹쳐보일 때는, plt.tight_layout() 를 적용해준다.
    image
    간단히 분석)
    여아(Female)의 경우 – 마지막글자가 e인 아이가 태어난 횟수는 시간이 50년씩 흐름에 따라 감소를 했다.
    남아의 경우에는 – 마지막 글자가 n인 아이가 50년씩 지나면서 거의 2배이상 증가했다.


데이터분석 7 – 특정 몇개의 글자에 대해, 연도에 따른 ,남아 출생횟수의 비중의 시간적변화를 분석해보자.

현재까지 구해진 last_letters_table 데이터를 이용할 것이다.

last_letters_table.head()
image


  • three_years_subtable에 적용했던 것처럼,
    데이터의 각 열방향의 총합( df.sum() )을 전체로 보고 –> 각 성분의 letters의 비중을 구하는 방식으로
    각 성별에 따른 / 모든 각 연도별(각 열별) 마지막글자 a~z까지의 출생횟수 총합에 대한 각 마지막글자 a,b,c,들의 출생횟수 비중을 구한다.

    letters_prop = last_letters_table / last_letters_table.sum()
    letters_prop.head()
    image


  • 이제 .loc []행인덱싱을 통해서 마지막글자가 d, n, y인 애들만 추출하고 / 열인덱싱의 경우에는 남아인 M열(계층중 상위계층)만 인덱싱하자.

    dny_prop = letters_prop.loc[["d", "n", "y"], "M"]
    image

    행이 짧고 열이 너무 기므로(1880~2014) 행과 열을 바꾸는 함수 .tranpose()를 적용시켜보자.
    dny_prop.transpose()
    image

    transpose()한 것을 바로 lineplot()을 그려보자
    dny_prop.transpose().plot()
    image
    간단한 해석) d와 y로 끝나는 남자아이는 비중의 변화가 별로 없던 것에 비해
    n으로 끝나는 남자아이의 출생횟수 변화는   1960년이 넘어가면서부터   비중이 드라마틱하게 늘어났다.


< pivot_table을 이용하면 아주 쉽고 빠르게 통계량을 구할 수 있다는 것에 주목하자>

+ Recent posts