728x90
반응형

지금까지 Index와 위치를 이용해서 데이터를 조회하는 방법에 대해 알아보았다. Index, 위치를 이용하여 행을 조회하는 것이 가장 빠르지만, 현실적으로 내가 원하는 데이터의 Index와 행의 위치를 미리 아는 것은 불가능하므로 이번 포스팅에서는 특정 조건으로 내가 원하는 행을 찾아내는 방법에 대해 학습해보도록 하겠다.

 

 

 

Pandas와 Boolean

불리언(Boolean)은 논리 자료형이라고도 하며, 참(True)과 거짓(False)을 나타낼 때 사용된다. Python에서 참(True)은 1, 거짓(False)은 0의 정수 값을 가지며, 이 특성을 잘 활용하면 재밌는 코드를 많이 만들어낼 수 있다.

>>> print(f"True의 Type: {type(True)}")
>>> print(f"False의 Type: {type(False)}")
>>> print("----"*10)
>>> print(f"True를 수치로 표현 시: {int(True)}")
>>> print(f"False를 수치로 표현 시: {int(False)}")
>>> print("----"*10)
>>> print(f"True를 수치로 표현 시: {float(True)}")
>>> print(f"False를 수치로 표현 시: {float(False)}")
True의 Type: <class 'bool'>
False의 Type: <class 'bool'>
----------------------------------------
True를 수치로 표현 시: 1
False를 수치로 표현 시: 0
----------------------------------------
True를 수치로 표현 시: 1.0
False를 수치로 표현 시: 0.0

Pandas는 Boolean Series를 이용하여 True인 행은 출력하고, False인 행은 출력하지 않을 수 있다. 이를 더 쉽게 이야기해보면, "내가 원하는 행만 True로 만들면 그 행만 조회할 수 있다"는 것이다.

 

 

 

 

1. 예제 데이터와 라이브러리 가지고 오기

※ 예제 데이터

이번 포스트에서도 지금까지 사용한 "시험점수.csv" 파일을 예제 데이터로 사용해보도록 하겠다.

사용할 라이브러리는 pandas지만, pandas와 함께 다니는 친구인 numpy도 혹시 쓸 일이 있을지 모르니 가져와주도록 하자.

시험점수.csv
0.00MB

 

>>> import pandas as pd
>>> import numpy as np

>>> FILE_PATH = "시험점수.csv"
>>> 시험점수 = pd.read_csv(FILE_PATH, encoding="euckr")
>>> 시험점수

  • 해당 예제 데이터는 이름, 성별, 학년, 반, 국어, 수학, 영어, 사회, 과학 총 9개의 열과 80개의 행으로 구성되어 있다.
  • 한글이 깨지는 경우, 위 코드처럼 pd.read_csv(File_path, encoding='euckr')로 불러오면 된다.

 

 

 

 

2. Series와 Boolean

  • DataFrame에서 조회할 대상을 가정해보자.

  • 조회할 대상: 3학년이고 2반에 있으며, 국어 점수가 70점 이상인 여학생을 찾는다.

  • 위 대상에 해당하는 속성이 4개나 되므로, 한 번에 찾기는 쉽지 않다.
  • 위 문구를 구성하는 조건을 최소 단위로 쪼개보자.

  • 조건 1: 학년 == 3
  • 조건 2: 반 == 2
  • 조건 3: 국어 >= 70
  • 조건 4: 성별 == "여자"

  • 문구를 최소 단위로 쪼개 보니 꽤 단순해졌다!
  • 방금 정의한 "조회할 대상"은 위 조건 4개에 대해 모두 참(True)인 행이다.
  • 즉, 위 조건 4개에 대해 모두 True인 행을 표현만 할 수 있다면, 위 행에 해당하는 사람의 데이터만 가지고 올 수 있다는 것이다.
  • 지금까지 우리는 Pandas에서 다양한 변수를 담고 있는 데이터(다변량 데이터) 타입인 DataFrame을 다뤄봤는데, 이번엔 단 하나의 변수만 담고 있는 데이터(단변량 데이터) 타입인 시리즈(Series)로 접근을 해보도록 하자.

 

 

 

2.1. 단변량 데이터를 담는 시리즈(Series)

  • 시리즈는 데이터 프레임에서 하나의 컬럼만 뽑으면 쉽게 만들 수 있다.
  • 데이터 프레임 "시험점수"에서 "성별" 시리즈를 뽑아보자.
>>> 시험점수["성별"]
0     남자
1     남자
2     남자
3     남자
4     남자
      ..
75    여자
76    여자
77    여자
78    여자
79    여자
Name: 성별, Length: 80, dtype: object
  • 시리즈는 각 Value에 대한 Index 정보, 시리즈의 이름(Name), 시리즈를 구성하는 원소의 수(Length), 시리즈의 데이터 타입(dtype) 정보를 담고 있다.
  • 이를 하나의 변수만 담고 있는 데이터 프레임이라고 생각해도 큰 문제는 없다.
    • 시리즈는 데이터 프레임에서 사용 가능한 함수의 대부분을 지원하므로, 데이터 프레임을 쓸 줄 안다면 큰 어려움 없이 익숙해질 수 있다.
    • 단, "시리즈 = 하나의 변수만 담긴 데이터 프레임"은 절대 아니다! 주의하도록 하자!
  • 시리즈는 DataFrame을 만들었던 방법처럼 list나 numpy array 등을 이용해 쉽게 만들 수 있다.
>>> Test_list = ["감", "바나나", "포도", "딸기"]
>>> pd.Series(data=Test_list, name="과일")
0      감
1    바나나
2     포도
3     딸기
Name: 과일, dtype: object

 

 

 

 

3. Boolean Series를 이용하여 원하는 행을 조회하기.

3.1. 단변량 데이터인 시리즈에 조건 걸기

  • "시험점수" 데이터 프레임에서 "성별" 시리즈를 뽑고, "여자"인 대상을 찾아보자.
  • Python은 기본적으로 다음과 같은 비교 연산자를 제공한다.
같다 다르다 크다 작다 이상 이하
기호 == != > < >= <=
  • 위 비교 연산자를 이용하여 "성별" == "여자"인 대상자를 찾아보자.
>>> 시험점수["성별"] == "여자"
0     False
1     False
2     False
3     False
4     False
      ...  
75     True
76     True
77     True
78     True
79     True
Name: 성별, Length: 80, dtype: bool
  • 비교 연산자를 사용하자 True와 False로 이루어진 Series가 출력되었다.
  • 이 Series에서 True는 "성별" == "여자"인 행을 뜻하며, False는 "성별" != "여자"인 행을 뜻한다.
  • 위에서 생성한 Boolean 시리즈를 이용해서, 제대로 탐색을 하였는지 확인해보도록 하자.
>>> 시험점수[시험점수["성별"] == "여자"].head(10)

  • 출력되는 결과가 매우 길어 df.head(n) 함수를 이용해 상위 n개의 행만 출력되게 하였다.
  • Pandas의 데이터 프레임은 df[Boolean Series]를 하면, Boolean Series에서 True에 해당하는 행만 출력한다.
  • 앞서, Boolean은 True는 1, False는 0의 값을 갖는다고 하였는데, 이 성질을 이용하면 "성별"=="여자"인 대상의 수를 알 수 있다.
>>> sum(시험점수["성별"] == "여자")
40
  • sum()은 괄호 안의 list, array, series 등의 Data type에 대해 합계를 내주는 함수로, 40이 나왔으므로 1이 40개 있다는 것을 보여준다.
  • 이는 "성별"=="여자"인 행이 40개 있다는 것으로, 여성의 수가 40명이라는 것을 의미한다.
  • 등호가 돌아가는 원리를 알았으니 이번엔 부등호를 조건으로 걸어보자.

 

  • 이번엔 부등호로 조회를 해보자.
  • 이번에는 국어 점수가 70점 이상인 대상을 찾아보자.
>>> 시험점수[시험점수["국어"] >= 70].head(10)

  • 국어 점수가 70점 이상인 상위 10명을 뽑아 보았다.
  • 부등호도 등호와 마찬가지로 쉽게 뽑을 수 있다.

 

 

 

 

3.2. 동시에 2개 이상의 조건 걸기

  • 앞서 시리즈를 이용해 단변량 데이터에 대해 조건을 걸어보았다.
  • 이번엔 2개의 시리즈를 동시에 사용하여 다변량 데이터에 대한 조건을 걸어보도록 하겠다.
  • 이때 사용되는 것이 옛날에 배웠던 집합(Set)이다.
  • Python에서 주로 사용되는 집합은 교집합, 합집합, 차집합이며, 이들은 다음과 같은 기호를 통해 사용할 수 있다.
집합 교집합 합집합 차집합
기호 & | -
  • 2개의 Boolean Series를 가지고 와보자.
>>> 여자_Boolean_Sr = 시험점수["성별"] >= "여자"
>>> 국어_Boolean_Sr = 시험점수["국어"] >= 70

>>> print(여자_Boolean_Sr)
>>> print("----"*10)
>>> print(국어_Boolean_Sr)
0     False
1     False
2     False
3     False
4     False
      ...  
75     True
76     True
77     True
78     True
79     True
Name: 성별, Length: 80, dtype: bool
----------------------------------------
0      True
1     False
2      True
3      True
4      True
      ...  
75    False
76     True
77    False
78    False
79     True
Name: 국어, Length: 80, dtype: bool
  • 두 Boolean 시리즈를 교집합 해보자.
>>> 여자_Boolean_Sr & 국어_Boolean_Sr
0     False
1     False
2     False
3     False
4     False
      ...  
75    False
76     True
77    False
78    False
79     True
Length: 80, dtype: bool
  • 두 시리즈 모두 True인 행만 True로 나오고, 둘 중 하나라도 False인 행은 False가 되었다.
  • 교집합은 두 가지에 모두 해당해야 한다는 의미이므로, 모두 True인 대상을 가지고 온다.
  • 즉, 두 조건에 동시에 해당하는 행만 교집합 시, True로 출력된다.
>>> 시험점수[여자_Boolean_Sr & 국어_Boolean_Sr].head(10)

  • 이를 이용하면, 조건이 아무리 많다고 할지라도 내가 원하는 대상을 구체적으로 찾아낼 수 있다.
  • 남은 조건인 "학년"==3, "반"==1을 추가하여 앞서 선언한 "조회할 대상"을 찾아보자.
>>> 시험점수[
        (시험점수["성별"] == "여자") &
        (시험점수["학년"] == 3) &
        (시험점수["반"] == 1) &
        (시험점수["국어"] >= 70)
        ]

  • 꽤 복잡한 조건이었음에도 Boolean Series를 사용하여 쉽게 찾아내었다.
  • 2개 이상의 Boolean Series를 변수에 담지 않고 바로 조회하려면 꼭 괄호 안에 넣어주기 바란다(괄호 미사용 시 에러 발생).

 

 

 

 

지금까지 조건에 해당하는 행 조회 방법에 대해 알아보았다. 다음 포스트에서는 Pandas의 str을 이용하여, 원하는 문자열에 해당하는 행을 탐색하는 방법에 대해 다뤄보도록 하겠다.

728x90
반응형
728x90
반응형

이전 포스트에서는 DataFrame의 Index를 기반으로 데이터를 조회하는 방법에 대해 알아보았다. 이번 포스트에서는 위치를 기반으로 데이터를 조회하는 슬라이싱(Slicing)과 iloc[]함수에 대해 알아보도록 하겠다.

 

 

 

 

슬라이싱(Slicing)을 이용한 데이터 조회

이전 포스트에서 데이터를 조회하는 방법으로 다음과 같은 방법이 있다고 소개하였다.

  1. 행의 고유한 key인 Index를 이용하여 내가 원하는 데이터 조회
  2. 데이터의 위치를 이용해서 데이터 조회(Slicing)
  3. 조건을 이용한 데이터 조회(Boolean)

이번 포스트에서는 "2. 데이터의 위치를 이용한 데이터 조회(Slicing)"에 대해 다뤄보도록 하겠다.

경우에 따라선 데이터의 위치가 조회에 있어 중요한 key로 작동하기도 한다. 그 예는 다음과 같다.

  • 고등학교 중간고사 전교 30등까지의 데이터를 조회
  • 30명이 있는 반에서 6 ~ 12번째 주번이 누구인지 조회
  • 월 소득의 상위 10% 데이터 조회 또는 하위 10% 조회

위와 같은 위치를 이용한 조회를 하려면 기본적으로 행의 순서가 일정한 규칙에 따라 나열되어 있어야 한다. 데이터가 아무런 규칙 없이 위치해 있다면 사용하기 힘들다. 때문에 규칙대로 데이터를 나열하는 정렬(Sort)과 함께 쓰이는 경우가 잦다.

Pandas의 DataFrame은 기본적으로 0부터 n까지의 index를 순서대로 자동 생성하므로, Index를 기반으로 한 조회와 위치를 이용한 조회의 차이를 알기 어려울 수 있으나 Index와 위치는 다음과 같은 차이를 갖는다.

  1. Index는 단순 행의 위치뿐만 아니라 다양한 정보를 담은 특정한 key로 만들 수 있다.
  2. 위치 기반 조회는 Index로부터 자유롭게 단순 위치로 데이터를 조회할 수 있다.

예제를 통해 위치를 이용한 데이터 조회를 해보도록 하자.

 

 

 

 

1. 예제 데이터 가지고 오기

※ 예제 데이터

이번 포스트에서도 저번 포스트에서 사용한 예제 데이터를 그대로 사용해보도록 하겠다.

시험점수.csv
0.00MB

>>> import pandas as pd
>>> import numpy as np

>>> FILE_PATH = "시험점수.csv"
>>> 시험점수 = pd.read_csv(FILE_PATH, encoding="euckr")
>>> 시험점수

  • 해당 예제 데이터는 이름, 성별, 학년, 반, 국어, 수학, 영어, 사회, 과학 총 9개의 열과 80개의 행으로 구성되어 있다.
  • 한글이 깨지는 경우, 위 코드처럼 pd.read_csv(File_path, encoding='euckr')로 불러오면 된다.

 

 

 

 

2. 순서를 이용해서 데이터를 가지고 오기

  • 순서를 이용해서 데이터를 가지고 오는 방법은 크게 2가지로 다음과 같은 코드를 사용할 수 있다.
  1. df[start:end]
  2. df.iloc[]
  • 두 함수는 기능은 거의 동일하나, 엄밀히 따지면 첫 번째 코드인 df[start:end]를 슬라이싱(Slicing)이라 하며, df.iloc[]는 어려운 말로 명시적 포지션 검색이라 한다.
  • 간단하게 말해서 첫 번째 녀석은 start부터 end까지를 잘라서 보여주는 것이고, 두 번째 녀석은 해당 위치에 있는 것을 가져오는 것으로 이전 포스팅에서 소개한 df.loc[]와 성격이 유사하다.

 

 

2.1. 슬라이싱을 통한 데이터 조회

  • 예제 데이터인 "시험점수"의 10번부터 20번까지 데이터를 조회해보도록 하자.
  • df[start:end]
시험점수[10:20]

  • 아주 간단한 코드를 통해 데이터를 자를 수 있다.
  • 슬라이싱을 통해 가지고 오는 행의 번호는 start 이상, end 미만이다.
  • 슬라이싱은 단순히 처음과 끝만 설정할 수 있는 것이 아니라 간격도 설정해줄 수 있다.
  • DataFrame 시험점수의 10번부터 20번까지를 2를 간격으로 조회해보도록 하자.
  • df[start:end:step]
>>> 시험점수[10:20:2]

  • start나 end 둘 중 하나를 지정해주지 않으면, "start부터 끝까지" 또는 "처음부터 end"까지로 슬라이싱할 수도 있다.
  • 68번째 이후의 데이터를 전부 조회해보고, 처음부터 15번째까지의 데이터를 조회해보도록 하자.
>>> 시험점수[68:]

>>> 시험점수[:15]

  • 여기서도 간격을 설정해줄 수 있다.
>>> 시험점수[:15:2]

 

 

 

 

2.2. 슬라이싱을 이용한 역순 조회

2.2.1. Step을 이용한 역순 조회

  • 슬라이싱의 각 인자들을 음수로 부여하면 역순으로 조회할 수 있다.
  • 전체 데이터를 역순으로 조회하려면 step을 -1로 지정하면 된다.
>>> 시험점수[::-1]

  • step을 -1로 설정하고 start를 지정해주면 start부터 역순 조회도 가능하다.
>>> 시험점수[4::-1]

  • step을 -1로 설정하고 end를 설정하면, 맨 뒤에서부터 시작하여 end까지로 조회된다.
>>> 시험점수[:4:-1]

  • step을 음수로 지정해주면, DataFrame의 순서를 깔끔히 뒤집어 조회하는 것을 볼 수 있다.

 

2.2.2. Step을 지정하지 않은 역순 조회

  • step을 음수로 지정하고 조회하면 데이터가 반대로 뒤집혀서 조회되는 것을 보았다.
  • step을 음수로 지정하지 않고 start나 end만 음수로 지정하면 데이터를 뒤집지 않은 본래의 상태에서 뒤에서부터 조회가 된다.
  • start를 음수 m으로 지정하고 end를 지정하지 않으면 뒤에서부터 m개의 행을 반환한다.
>>> 시험점수[-4:]

  • start를 지정하지 않고 end를 음수 n으로 지정하면 뒤에서부터 n개의 행을 제외하고 반환한다.
>>> 시험점수[:-4]

  • start를 음수 m으로 end를 음수 n으로 입력 시 뒤에서부터 m:n의 행을 가지고 온다.
  • 단, start의 음수 m이 end의 음수 n보다 커야 한다(그렇지 않다면, 행이 하나도 조회되지 않는다).
>>> 시험점수[-7:-4]

>>> 시험점수[-4:-4]

>>> 시험점수[-2:-4]

 

 

 

 

2.3. 슬라이싱과 인덱스

  • 슬라이싱은 기본적으로 행의 순서를 기반으로 행을 조회하지만, 인덱스를 대상으로도 조회가 가능하긴 하다.
  • 시험점수 DataFrame의 "이름"칼럼을 Index로 설정하고, "지원"부터 "민서"까지의 행을 조회해보자.
>>> 시험점수.set_index("이름", inplace=True)
>>> 시험점수["지원":"민서"]

  • 해당 기능은 앞서 다뤘던 Index의 범위를 이용한 조회와 유사하다.
>>> 시험점수.loc["지원":"민서"]

  • 다만, 해당 방법은 중복이 있는 Index에 대해서 Slicing을 할 곳을 특정할 수 없으므로, 동작하지 않는다.
>>> 시험점수.loc["영호":"하윤"]

  • 또, 인덱스를 다른 숫자로 정의한다면, 인덱스를 무시하고 순서로 슬라이싱을 한다.
# 30에서 110까지의 정수를 새로운 인덱스로 정의하였다.
>>> 시험점수.reset_index(drop=False, inplace=True)
>>> 시험점수.index = range(30, 110)
>>> 시험점수

>>> 시험점수[30:40]

  • 30에서 110으로 Index를 바꿨으므로, Index로 슬라이싱 되면 30에서 40까지의 Index로 출력되어야 하나, 30번째 행에서 40번째 행에 위치한 Index인 60~69가 출력되었다.

 

 

 

 

2.4. df.iloc[]를 이용한 조회

  • 앞서 슬라이싱을 이용한 순서 기반 조회를 해보았다.
  • 슬라이싱은 Pandas의 DataFrame, Series뿐만 아니라 list, numpy array 등 Python의 다양한 data type에서 지원하는 기능이지만, Pandas는 여기에 더해서 df.iloc[] 또는 Sr.iloc[] 함수를 통해 위치 기반 조회를 할 수 있다.
>>> 시험점수.reset_index(drop=True, inplace=True)
>>> 시험점수.loc[5]
이름     순자
성별     여자
학년      1
반       1
국어     80
수학    100
영어     75
사회     70
과학     65
Name: 5, dtype: object
>>> 시험점수.loc[5:10]

  • iloc는 기본적으로 슬라이싱과 기능이 매우 유사하므로, 슬라이싱에서 다뤘던 기능들이 대부분 동일하게 실행된다.
  • 단, iloc는 인덱스 기반 조회는 불가능하다.
>>> 시험점수.set_index("이름", inplace = True)
>>> 시험점수.iloc["지원":"민서"]

 

 

 

이번 포스트에서는 위치를 기반으로 필요한 행을 조회하는 슬라이싱과 iloc함수를 알아보았다. 다음 포스트에서는 특정 조건을 기반으로 행을 조회하는 Boolean 기반 행 조회 방법에 대해 알아보도록 하자.

728x90
반응형
728x90
반응형

얼마만의 포스팅인지... 가장 최근에 한 포스팅이 2021년 05월 17일이니, 반년 조금 넘는 기간 동안 포스팅을 안 했다.

나름의 변명이라면 올해 4월에 새로운 직장에 가고, 프로젝트를 하나 둘 맡아서 하다 보니 정신이 없기도 했고, 프로젝트를 어느 정도 마무리 짓고 여유가 나자 그놈의 디아블로 2가 나와버리는 바람에 시간이 슉 지나가버렸다.

너무도 감사하고, 죄송스럽게도 많은 분들께서 찾아와 주셔서, 미약한 실력이나마 도움이 될 수 있기를 바라며 다시 한번 포스팅을 해보고자 한다. 과연, 포스팅하고자 했던 것들을 전부 올리고, 새로운 영역을 파헤쳐 갈 수 있을까?

 

 

 

 

데이터 프레임, 데이터 조회하기

Python을 사용하는 데이터 분석가에게 가장 기초가 되는 Pandas를 다룬 것이 올해 초인 2021.02.24일이었으니, 다음에 뭘 다루려 했었는지 가물가물하다. 앞서 DataFrame의 Index와 Column명을 가지고 노는 법에 대해 알아봤으니, 이번 포스팅에서는 Index와 Column명을 이용해서 내가 원하는 데이터만 가지고 오는 법에 대해 알아보도록 하겠다.

데이터 프레임의 가장 큰 장점은 누구든지 이해하기 쉬운 형태로 데이터를 볼 수 있다는 것이지만, 데이터의 양이 많아질수록 한눈에 들어오지 않을 정도로 데이터가 거대해지기 때문에, 내게 필요한 데이터만 조회하는 법을 알아야 한다.

데이터 프레임의 조회 방식은 크게 index와 column 명으로 조회하는 방법, 데이터의 순서를 기반으로 조회하는 방법,  Boolean으로 조회하는 방법이 있는데, 이를 찬찬히 알아보도록 하자.

 

내가 원하는 데이터를 가지고 오기

  • 내가 원하는 데이터를 가지고 오는 방법은 크게 3 가지가 있다.
  1. 내가 원하는 행을 가지고 오기
  2. 내가 원하는 열을 가지고 오기
  3. 내가 원하는 행과 열을 가지고 오기
  • 여기서 내가 원하는 행을 가지고 오는 방법이 비교적 복잡하며, 원하는 행을 가지고 온 후 내가 필요한 열을 설정해주면, 문제는 쉽게 해결된다.
  • 그러므로, 행을 가지고 오는 방법을 위주로 다뤄보도록 하자.
  • Pandas의 DataFrame에서 내가 원하는 행을 가지고 오는 방법도 크게 3가지가 있다.
  1. Index를 이용해서 가지고 오기(loc)
  2. 위치를 이용해서 가지고 오기(슬라이싱, iloc)
  3. 조건을 이용해서 가지고 오기(Boolean)
  • 이번 포스트에서는 Index를 이용해 내가 원하는 데이터를 가지고 오는 방법에 대해 다뤄보도록 하자.

 

 

 

 

1. 예제 데이터 가지고 오기

※ 예제 데이터

해당 예제 데이터는 이름, 성별, 학년, 반, 국어, 수학, 영어, 사회, 과학 총 9개의 열(Column)과 80개의 행(Row)으로 구성된 csv파일이다. 당분간 이 데이터로 학습을 진행해보도록 하자.

시험점수.csv
0.00MB

  • 다운로드 받은 위 예제 데이터("시험점수.csv")파일을 내 워킹 디렉토리(Working Directory)로 이동시키거나, 파일의 경로를 이용해서 불러와보도록 하자.
>>> import pandas as pd
>>> import numpy as np

>>> FILE_PATH = "시험점수.csv"
>>> 시험점수 = pd.read_csv(FILE_PATH, encoding="euckr")
>>> 시험점수

  • R과 비교되는 파이썬의 가장 큰 장점은 한글 사용이 비교적 자유롭다는 것이다.
  • 파이썬은 utf-8 기반으로 만들어져 있기 때문에 데이터뿐만 아니라 변수명을 한글로 써도 큰 문제가 없다.
  • 혹시 모르는 에러가 발생할 위험이 있으므로, 그래도 영어를 사용하는 것을 추천한다!!
  • 한글이 들어 있는 파일이라면 파일의 경로만을 이용해서 데이터를 가져오는 경우, 한글이 깨지는 경우가 있는데, 이는 대부분 Encoding 문제로, pd.read_csv() 함수의 encoding 파라미터를 "euckr"로 지정하면 거의 해결된다.

 

 

 

 

2. Index를 이용해서 데이터를 가지고 오기.

  • 데이터 프레임에서 내가 원하는 행을 조회하는 가장 빠른 방법은 Index를 이용하는 것이다.
    • Index는 유일하게 존재하는 고유한 값일 수도 있으나, 중복된 값일 수도 있다.
    • Index를 이용한다면, 데이터 조회뿐만 아니라 데이터 병합(Merging)도 쉽게 할 수 있다.
  • Index로 조회하는 방법은 다음과 같다.
  • df.loc[index]: index에 해당하는 DataFrame의 행을 가지고 온다.

 

 

2.1. 하나의 index를 가지고 와보자.

  • index 60번을 가지고 와보자.
>>> 시험점수.loc[60]
이름    민준
성별    남자
학년     4
반      1
국어    90
수학    75
영어    80
사회    70
과학    60
Name: 60, dtype: object

>>> type(시험점수.loc[60])
pandas.core.series.Series
  • 하나의 index를 조회하자, Series Type으로 해당 index 정보가 조회되는 것을 볼 수 있다.
  • 이 시리즈에서 이름을 하나의 값으로 가지고 와보도록 하자.
>>> Index60_Sr = 시험점수.loc[60]
>>> Index60_Sr["이름"]
'민준'
  • 파이썬의 딕셔너리(Dictionary) 객체처럼 내가 원하는 변수명을 key로 주니, 내가 원하는 값이 반환되는 것을 볼 수 있다.

 

 

 

 

2.2. 여러 개의 index를 가지고 와보자.

  • 이번에는 여러 개의 index를 한 번에 가지고 와보도록 하자.
  • 15, 20, 30, 45번의 Row를 가지고 와보도록 하겠다.
  • 여러개의 index를 한번에 조회는 내가 원하는 index들이 들어있는 list를 만들어 loc에 넣으면 된다.
>>> 시험점수.loc[[15,20,30,45]]

  • 이전과 다르게 이번에는 index에 해당하는 데이터 프레임으로 결과가 조회되는 것을 볼 수 있다.
  • 위 방법을 응용하면 Index A부터 Index B까지 조회도 가능하다.
>>> index_range = range(15,20) 
>>> 시험점수.loc[index_range]

>>> 시험점수.loc[15:20]

  • 다만 Index의 범위를 이용하여 탐색하는 방법은 위 방법보다 다음에 다룰 Slicing이 더 많이 사용되므로, 이런 것도 있구나~ 하고 넘어가도록 하자.

※ 주의 사항

  • 일반적인 파이썬 슬라이싱 방식은 start:end 일 경우, start부터 end-1까지의 연속된 값을 반환하나, .loc[start:end]는 start 부터 end 까지의 연속된 index를 조회한다.
시험점수[15:20]

  • 일반적인 슬라이싱 방식은 위 방식으로 하며, DataFrame.loc[Index]는 Index에 속해 있는 모든 index의 행을 가지고 온다.

 

 

 

 

2.3. index와 column으로 조회해보자.

  • 해당 Index에서 특정 Column의 데이터만 보고 싶다면, index와 column을 동시에 넣어 조회하면 된다.
  • 앞서 소개한 df.loc[index]를 아래와 같이 쓰면 된다.
    • df.loc[index, column]
  • 16번 index의 국어 점수를 조회해보자.
>>> 시험점수.loc[16, "국어"]
75
  • 해당 방법을 통해 내가 필요한 값을 하나의 원소로 출력하였다.
  • 이번에는 index 16의 이름, 학년, 반, 국어 점수를 동시에 출력해보도록 하자.
>>> 시험점수.loc[16, ["이름", "학년", "반", "국어"]]
이름    정숙
학년     1
반      2
국어    75
Name: 16, dtype: object
  • 내가 필요한 index들을 list에 담아 조회하였듯, 조회하고자 하는 column들을 list에 넣어 조회하면 해결된다.
  • 이번엔 index를 16, 20, 25로 column을 이름, 학년, 반, 국어로 조회해보자.
>>> 시험점수.loc[[16, 20, 25], ["이름", "학년", "반", "국어"]]

 

 

 

 

2.4. 값을 Index에 넣어 조회해보자.

  • 앞서 다뤘던 방법들은 내가 원하는 Index를 알고 있다는 전제하에 유용하지만, 대부분 Index는 자동으로 설정되는 0~n 사이의 값이므로 이를 활용하는 것은 쉽지 않다.
  • 이럴 땐, 특정 열을 Index로 만들어 조회하는 것으로 이 문제를 해결할 수 있다.
  • "이름" column을 Index로 만들어보자.
  • df.set_index(column, drop=True, inplace=False): column을 df의 index로 설정한다.
>>> 시험점수.set_index("이름", inplace=True)
>>> 시험점수

  • inplace = True로 설정하면, df.set_index의 대상인 df에 바로 이를 적용할 수 있다.
  • default인 inplace를 False로 하면 df.set_index의 결과만 출력한다.
  • drop=False로 설정하면, 기존 index를 제거하지 않고 데이터 프레임의 열로 추가한다.
  • index를 "영철"로 조회해보자.
>>> 시험점수.loc["영철"]

  • DataFrame의 Index는 고유한 값이 아니어도 설정 가능하므로, 위와 같이 "이름"이 "영철"에 해당하는 Row를 모두 가지고 오는 것을 볼 수 있다.
  • Index를 초기 상태로 돌려보자.
>>> 시험점수.reset_index(drop=False, inplace=True)
>>> 시험점수

  • 이번에는 학년이 1학년인 사람들만 조회해보도록 하자.
>>> 시험점수.set_index("학년", inplace=True)
>>> 시험점수.loc[1]

 

 

 

 

2.5. 다중 값을 Index로 조회해보자.

  • 지금까지 하나의 열만 Index로 넣어 조회를 해보았다.
  • 그렇다면, 여러 개의 열을 Index에 넣어 내가 원하는 행만 조회할 수는 없을까?
  • 이때는 크게 2가지 방법이 있다.
  1. Multi Index를 만들어 조회하기
  2. 조회를 위한 열을 만들기
  • 먼저 Multi index를 만드는 법부터 알아보도록 하자.
  • Index를 다시 초기화하고 학년과 반으로 Multi index를 만들어보도록 하겠다.
>>> 시험점수.reset_index(drop=False, inplace=True)
>>> 시험점수.set_index(["학년", "반"], inplace=True)
>>> 시험점수

  • 2학년 1반 사람들을 조회해보도록 하자.
  • Multi Index는 Python의 Tuple로 생성되므로, Tuple로 조회하면 된다.
>>> 시험점수.loc[(2,1)]

  • 3개 이상의 열을 Multi Index로 만들어 조회도 가능하다.
  • 이번에는 학년, 반, 이름으로 Multi Index를 만들어보자.
>>> 시험점수.reset_index(drop=False, inplace=True)
>>> 시험점수.set_index(["학년", "반", "이름"], inplace=True)
>>> 시험점수

  • 이번에는 2학년 1반의 영철을 조회해보도록 하자.
>>> 시험점수.loc[(2,1,"영철")]
성별     남자
국어    100
수학     80
영어     75
사회     80
과학     45
Name: (2, 1, 영철), dtype: object
  • 위와 같은 방법을 통해 내가 원하는 Column을 Index로 만들어 조회해보았다.
  • 다만, 해당 방법의 한계점은 특정한 하나의 값만 조회가 가능하므로, 수학 점수와 같은 연속형 데이터의 범위에 대해서는 조회가 불가능하다는 단점이 있다.
  • 물론, 이용해 우회하여 해결할 수 있기는 하다.

 

 

 

 

2.6. 새로운 열을 만들어 조회하기

  • 지금까지는 기존에 존재하는 열을 그대로 Index로 만들어 조회를 하였다.
  • 이번에는 내가 필요로 하는 값에 대한 변수를 만들고, 이 변수를 Index로 만들어 조회를 해보도록 하겠다.
  • 수학 점수가 70점 이상인 사람들을 표시하는 새로운 변수를 만들어보자.
    • 조건을 이용한 새로운 변수 만들기는 추후 자세히 다룰 테니 이번엔 단순 참고용으로 보도록 하자.
>>> 시험점수["수학_mask"] = np.where(시험점수["수학"] >=70, "수학_70점_이상", "수학_70점_미만")
>>> 시험점수

  • 70점 이상은 "70점_이상", 70점 미만은 "70점_미만"인 수학_mask"라는 변수를 생성하였다.
  • 새로 만든 "수학_mask"변수를 이용해서 70점 이상인 사람들만 출력해보도록 하자.
>>> 시험점수.set_index("수학_mask", inplace=True)
>>> 시험점수.loc["수학_70점_이상"]

  • 이미지가 너무 커서 일부만 표시해보았다. 길이가 48인 결과가 나왔다면, 제대로 나온 것이다.
>>> len(시험점수.loc["수학_70점_이상"])
48
  • 이번엔 영어 점수가 70점 이상인 사람을 추가해보자.
>>> 시험점수.reset_index(drop=False, inplace=True)
>>> 시험점수["영어_mask"] = np.where(시험점수["영어"] >=70, "영어_70점_이상", "영어_70점_미만")
>>> 시험점수

  • 이번엔 수학 점수가 70점 이상이면서, 영어 점수도 70점 이상인 사람을 출력해보자.
  • Multi Indexing을 사용할 수도 있으나, 이번에는 수학_mask와 영어_mask를 하나로 합쳐서 Index를 만들어보겠다.
>>> 시험점수["key"] = 시험점수["수학_mask"] + "&"+ 시험점수["영어_mask"]
>>> 시험점수.set_index("key", inplace=True)
>>> 시험점수.loc["수학_70점_이상&영어_70점_이상"]

  • 이미지가 너무 커서 일부만 표현하였으며, 길이가 35가 나오면 정상이다.
>>> len(시험점수.loc["수학_70점_이상&영어_70점_이상"])
35
  • 위와 같은 새로운 변수를 만들어 Index로 바꾸는 방법은 이해를 돕기 위해 변수를 지저분하게 만들었지만, 이보다 깔끔하게 할 수 있는 방법을 다음에 포스팅해보도록 하겠다.

 

 

 

지금까지 Index를 이용한 데이터 프레임 행 조회 방법에 대해 알아보았다. 다음 포스트에서는 위치를 이용한 행 조회에 대해 알아보도록 하겠다.

728x90
반응형
728x90
반응형

중심경향치(Center Tendency)


◎ 중심경향치: 관찰된 자료들이 어디에 집중되어 있는지를 나타낸다.


 통계학은 기본적으로 데이터가 어디에 모이고, 얼마나 흩어지는지를 통해서 데이터의 성격을 파악한다. 중심경향치는 데이터가 어디에 모이는지를 보는 것으로, 최빈값, 중앙값, 평균 등의 다양한 지표를 이용하여, 데이터가 모인 곳을 찾아낸다.

 그렇다면, 그 데이터가 모이고 흩어진다는 것이 대체 무슨 말일까? 이를 알기 위해, 이전 포스트에서 학습했던 내용을 바탕으로 이를 눈으로 확인해보자.

 

 

 

 

0. 데이터가 모이고 흩어진다는 것은 무엇일까?

  • 이전 포스트에서 사용했던 데이터를 가지고 오고, 모든 변수들을 히스토그램으로 시각화해보자.

Data_for_study.csv
3.39MB

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
Rawdata = pd.read_csv("Data_for_study.csv")
plt.rc('font', family='NanumGothic')
Rawdata.hist(figsize=(16,25), layout=(5,3), grid=False,density=True)

plt.show()

  • 위 코드는 pandas와 matplotlib.pyplot 두 가지를 사용하여, DataFrame 내에 있는 변수들을 시각화해주며, 히스토그램은 그 변수가 무엇이든 간에, 그 변수의 빈도를 이용해 그래프를 그린다. 또한, bins 파라미터를 통해, 키, 몸무게 같은 비율 척도는 실제 형태보다 단순화시켜, 모든 변수들의 추이를 쉽게 파악할 수 있다.
  • 위 히스토그램들을 보면, "주중_인터넷이용시간"은 주로 0 ~ 100 사이에 가장 많은 데이터가 모여 있으며, 흩어진 정도는 그리 크지 않다는 것을 알 수 있다.
  • 이러한 변수별 데이터가 어디에 모여있는지를 하나의 값으로 확인할 수 있는 방법이 바로 중심경향치다.

 

 

 

 

1. 최빈값(Mode)


◎ 최빈값(Mode): 빈도수가 가장 큰 관찰값


  • 도수분포표에서 가장 값이 많이 모여 있는 관찰 값을 의미한다.
  • 양적 자료, 질적 자료에서 모두 사용되나, 일반적으로 질적 자료에서 더 자주 사용된다.
  • 위에서 불러온 데이터에서 명목변수: "흡연경험", 등간변수(리커트 5점 척도): "스트레스인지", 비율 변수: "몸무게"에 대하여 최빈값을 구해보자.

 

1.1. 도수분포표를 사용하여 최빈값 구하기

  • 최빈값을 구하는 방법은 도수분포표를 구하고, 가장 빈도수가 높은 관찰 값을 선택하는 방법이 있다.
  • 연속형 변수를 구간 화하는 것은 꽤 귀찮은 일이므로, 20개 이상의 관찰 값을 갖는 경우, 10개의 구간을 갖는 변수로 변환하여 도수분포표를 출력하는 함수를 만들었다.
def make_freq_table(data, column):
    """
    -------------------------------------------------------------------------------
    지정된 변수의 관찰값이 20개보다 많은 경우, 10개의 등급을 가진 데이터를 반환한다.
    -------------------------------------------------------------------------------
    Input: DataFrame, str
    Output: DataFrame
    """
    # array 생성
    target_array = data[column].to_numpy()

    # class의 수가 20개보다 많은 경우 10개로 줄인다.
    class_array = np.unique(target_array)

    if len(class_array) > 20:

        min_key = class_array.min()
        max_key = class_array.max()
        split_key = np.linspace(min_key, max_key, 10)

        a0 = str(round(split_key[0], 2)) + " 이하"
        a1 = str(round(split_key[0], 2)) + " ~ " + str(round(split_key[1], 2))
        a2 = str(round(split_key[1], 2)) + " ~ " + str(round(split_key[2], 2))
        a3 = str(round(split_key[2], 2)) + " ~ " + str(round(split_key[3], 2))
        a4 = str(round(split_key[3], 2)) + " ~ " + str(round(split_key[4], 2))
        a5 = str(round(split_key[4], 2)) + " ~ " + str(round(split_key[5], 2))
        a6 = str(round(split_key[5], 2)) + " ~ " + str(round(split_key[6], 2))
        a7 = str(round(split_key[6], 2)) + " ~ " + str(round(split_key[7], 2))
        a8 = str(round(split_key[7], 2)) + " ~ " + str(round(split_key[8], 2))
        a9 = str(round(split_key[8], 2)) + " 이상"
        new_index = [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]


        target_array= np.where(target_array <= split_key[0], 0,
                               np.where((target_array > split_key[0]) & (target_array <= split_key[1]), 1,
                                        np.where((target_array > split_key[1]) & (target_array <= split_key[2]), 2,
                                                 np.where((target_array > split_key[2]) & (target_array <= split_key[3]), 3,
                                                          np.where((target_array > split_key[3]) & (target_array <= split_key[4]), 4,
                                                                   np.where((target_array > split_key[4]) & (target_array <= split_key[5]), 5,
                                                                            np.where((target_array > split_key[5]) & (target_array <= split_key[6]), 6,
                                                                                     np.where((target_array > split_key[6]) & (target_array <= split_key[7]), 7,
                                                                                              np.where((target_array > split_key[7]) & (target_array <= split_key[8]), 8, 9)))))))))


    # 도수분포표 생성
    freq_table = pd.DataFrame(pd.Series(target_array).value_counts(), columns = ["freq"])
    freq_table.index.name = column

    freq_table.sort_index(inplace = True)
    freq_table["ratio"] = freq_table.freq / sum(freq_table.freq)
    freq_table["cum_freq"] = np.cumsum(freq_table.freq)
    freq_table["cum_ratio"] = np.round(np.cumsum(freq_table.ratio), 2)
    freq_table["ratio"] = np.round(freq_table["ratio"], 2)

    if "new_index" in locals():
        freq_table.index = new_index
        freq_table.index.name = column

    return freq_table
  • np.linspace(start, end, num): start부터 end까지 num개를 일정한 간격으로 자르는 함수로, 아주 간편하게, 연속형 데이터를 범주화할 수 있다.
  • 흡연경험의 도수분포표
make_freq_table(Rawdata, "흡연경험")

  • 명목 변수인 흡연경험의 최빈값은 1.0인 것을 알 수 있다. 청소년건강행태조사 이용지침서 참고 시, "없다"가 88%로 가장 많이 등장하였다.
make_freq_table(Rawdata, "스트레스인지")

  • 등간 변수(리커트 척도)인 스트레스인지의 최빈값은 3.0인 것을 알 수 있다. 청소년건강행태조사 이용지침서 참고 시, "조금 느낀다"가 가장 많이 등장하였다.
make_freq_table(Rawdata, "몸무게")

  • 비율 변수인 몸무게에서 제일 많이 등장한 등급(Class)는 45.87 ~ 56.3 kg인 것을 알 수 있다. 표본집단인 중·고등학생 중 36%가 해당 구간에 존재한다.

 

1.2. 파이썬을 이용하여 최빈값 구하기

  • 도수분포표를 일일이 구하고, 최빈값을 구하는 일은 꽤 번거로운 일이다.
  • 데이터 분석에서 기본적으로 사용되는 라이브러리 중 하나인 pandas는 다양한 기본 함수를 제공하여, 이러한 문제를 쉽게 해결할 수 있게 해 준다.
  • Series.mode(): 최빈값을 출력한다.
>>> 흡연경험_최빈값 = Rawdata.흡연경험.mode()
>>> 흡연경험_최빈값
0    1.0
dtype: float64


>>> 스트레스인지_최빈값 = Rawdata.스트레스인지.mode()
>>> 스트레스인지_최빈값
0    3.0
dtype: float64


>>> 몸무게_최빈값 = Rawdata.몸무게.mode()
>>> 몸무게_최빈값
0    60.0
dtype: float64
  • Series.mode()는 Series로 결과를 출력한다.
  • 양적 변수라 할지라도, 바로 최빈값을 찾는 경우, 굳이 도수분포표를 만드는 수고를 할 필요가 없으므로, 구간을 만드는 수고를 하지 않아도 된다.
  • 이번에는, 최빈값에 해당하는 빈도수를 출력해보자.
# 최빈값과 최빈값 도수 출력
def mode_value_printer(data, column):
    
    mode_ = data[column].mode().values[0]
    freq =len(data[data[column] == mode_])
    
    print(f"{column} - 최빈값: {mode_}, 도수: {freq}")
>>> mode_value_printer(Rawdata, "흡연경험")
흡연경험 - 최빈값: 1.0, 도수: 48995

>>> mode_value_printer(Rawdata, "스트레스인지")
스트레스인지 - 최빈값: 3.0, 도수: 22915

>>> mode_value_printer(Rawdata, "몸무게")
몸무게 - 최빈값: 60.0, 도수: 2350
  • 보시다시피 pandas 기본 함수를 사용하면, 아주 쉽게 최빈값과 그에 해당하는 도수를 찾을 수 있다.
  • 그러나, 양적 변수, 그중에서도 관찰 값이 매우 많은 변수는 범주화를 시키는 것과, 단순하게 가장 많이 등장한 관찰 값을 찾는 것이 다른 결과를 가져온다.
  • 때문에 양적 변수에서는 중심경향치를 확인하고자 할 때, 최빈값보다는 평균, 중위수와 같은 다른 값을 추출하는 경우가 더 많다(물론, 연구자의 의도에 따라 최빈값 역시 필요할 수 있으므로, 절대 양적 변수에 최빈값을 사용하지는 않는다고 생각해선 안된다).

 

 

 

 

2. 중앙값(Median)


◎ 중앙값(Median): 수치로 된 자료를 크기 순서대로 나열할 때, 가장 가운데에 위치하는 관찰값을 말한다.

$$Md = \frac{(n+1)}{2}$$


  • 중앙값은 순서, 일정한 간격을 가지고 있는 양적 변수에 대해서만 사용 가능하며, 말 그대로 한 변수 내 모든 관찰값들의 중앙에 있는 값을 가리킨다.
  • 중앙값에서 이슈라고 할 수 있는 것은 관찰값의 수가 짝수인지 홀수인지로, 아래 예시를 보자.

$$ A = {1, 3, 4, 7, 8, 10, 11, 15, 16}$$

  • 위 예시 같이 집합 내 원소의 수가 홀수인 경우에는 그냥 $\frac{9+1}{2}=5$에 있는 관찰값을 중앙값으로 하면 되지만, 짝수인 경우는 조금 다르다.

$$ B = {2, 4, 6, 7, 9, 10} $$

  • 위 예시 같이 집합 내 원소의 수가 짝수인 경우에는 $\frac{6+1}{2} = 3.5$가 되어, 3.5번째에 있는 관찰값을 중앙값으로 사용해야 하나, 3.5번째 관찰값은 존재할 수 없다.
  • 이 때는, 3번째 관찰값인 6과 4번째 관찰값인 7의 평균을 중앙값으로 사용한다. 즉, $\frac{6+7}{2} = 6.5$가 중앙값이 된다.

 

2.1. 도수분포표를 이용하여 연속형 데이터의 중앙값 구하기

  • 중앙값은 관찰값들의 중앙에 있는 값이므로, 도수분포표를 사용하지 않고 구할 수 있고, 그것이 정석이다.
  • 그러나, 항상 모든 관찰값들을 알 수 있는 것이 아니고, 때에 따라서는 도수분포표를 사용해서 중앙값을 유추해야할 필요도 있다.
    (물론, 원시자료를 손 델 수 있다면, 굳이 그럴 필요는 없지만!)
  • 이번에는 범주화된 연속형 데이터의 도수분포표를 이용해서 중앙값을 구해보자.
  • 위에서 만든 make_freq_table함수를 이용해서 키에 대한 도수분포표를 만들어보자.
make_freq_table(Rawdata, "키")

  • 총데이터의 양은 55748개이며, 55748의 중앙값은 $\frac{55748+1}{2} = 27874.5$이다. 즉, 27,874.5번째에 있는 값이 있는 구간이 중앙값이다.
  • 누적빈도를 볼 때, 27,874.5는 162.67 ~ 169.33 구간에 존재하므로, 중앙값이 있는 구간은 162.67 ~ 169.33임을 알 수 있다.
  • 이 구간 안에서 비율을 사용해서 중앙값을 유추해보자

  • 위 방법처럼 관찰 값의 비율과 빈도의 비율을 이용하면, 중위수를 유추해낼 수 있다.
  • 실제 중위수랑 비교해보자.
>>> Rawdata.키.median()
165.0
  • 실제 중위수와 도수분포표를 사용해서 유추한 중위수가 상당히 유사한 것을 알 수 있다.

 

2.2. 파이썬을 이용하여 중위수 구하기

  • 파이썬을 이용해 중위수를 구하는 것은 정말 단순하다.
>>> Rawdata.스트레스인지.median()
3.0

>>> Rawdata.몸무게.median()
57.0
  • Series.median()을 사용하면, 중위수를 구할 수 있다.
  • numpy 함수를 사용하는 경우는 다음과 같다.
>>> np.median(Rawdata.스트레스인지.to_numpy())
3.0

>>> np.median(Rawdata.몸무게.to_numpy())
57.0
  • np.median(array)를 사용해서 중위수를 구하면 된다.

 

 

 

 지금까지 최빈값과 중앙값을 구해보았다. 다음 포스트에서는 가장 대표적인 중심경향치인 평균에 대해 자세히 알아보도록 하겠다.

728x90
반응형
728x90
반응형

도수분포표(Frequency Distribution Table)


◎ 도수분포표: 수집된 데이터를 분류한 후, 각 분류에 해당하는 데이터의 빈도, 비율 등으로 정리한 표를 말한다.


 앞서 통계학은 크게 기술통계학(Descriptive statistics)과 추론통계학(Inferential statistics) 이 두 가지로 나뉘며, 이 둘은 별개의 존재가 아니라, 기술통계학 > 추론통계학순으로 순차적으로 이루어진다고 하였다.

 기술통계학은 말 그대로 데이터가 가지고 있는 정보를 기술(Describe)하는 것이며, 도수분포표는 데이터의 각 범주별 빈도수와 비율을 이용하여, 데이터를 설명하는 방법으로 기술통계학의 기초가 되는 기법이다.

 백 마디 말보다, 한 번 실제 만들어보는 것이 가장 좋은 방법이니, 실제로 도수분포표를 만들어보고, 도수분포표가 어떻게 생겼고, 도수분포표를 이용해서 무엇을 할 수 있기에 기술통계학의 기초가 되는 것인지 알아보도록 하자.

  • 도수분포표를 학습할 때, 집단을 잘 구분하는 것이 중요하다.
  • 한 변수 안에는 $m$개의 데이터가 존재하는데, 이 $m$개의 데이터를 중복을 제거하면 $n$개의 데이터가 남게 된다($m$ ≥ $n$).
  • 이 중복이 없는 $n$의 데이터는 한 원소 안의 집단(Group), 등급(Class) 두 가지 용어로 부를 수 있는데, 본 포스트에서는 이해하기 쉽도록, 선택된 군은 집단(Group)으로, 한 변수 안의 중복이 제거된 데이터의 군은 등급(Class)라 부르겠다.

 

 

 

 

1. Python을 사용하여, 기본적인 도수분포표를 만들어보자.

1.1. 데이터 가지고 오기

 이전 포스트(참고: "통계 분석을 위한 데이터 준비")에서 생성한 데이터를 기반으로 통계 분석을 진행하도록 하겠다. 해당 데이터는 청소년건강행태조사 2019년 데이터에서 대표적인 16개 변수만 선택하고, 간단하게 결측값을 처리한 데이터다. 보다 상세한 설명이 필요한 경우 위 참고를 보길 바란다.

Data_for_study.csv
3.69MB

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
  • 데이터 분석을 할 때, 기본적으로 사용하는 라이브러리다.
  • pandas는 DataFrame을 이용해, 데이터를 관리하는 모든 과정을 굉장히 편하게 해 준다.
  • numpy는 빠른 수학 연산에 필수인 라이브러리다.
  • matplotlib.pyplot은 시각화에 필수인 라이브러리다.
Rawdata = pd.read_csv("Data_for_study.csv")
Rawdata
  • pd.read_csv("파일이름.csv"): csv 파일을 DataFrame으로 불러온다.

  • 변수(Column)의 수는 총 14개이며, 55,748개의 대상이 들어가 있는 데이터가 불러와졌다.

 

1.2. 질적 변수 도수분포표 만들기

  • 출력된 위 데이터만으로 데이터가 어떻게 생겼고, 그 안에 숨어있는 정보를 찾아내는 것은 불가능에 가깝다.
  • 위 데이터를 가장 쉽게 정리하는 방법이 바로 도수분포표(Frequency distribution table)이다.
  • 성별에 대한 도수분포표를 만들어보겠다.
>>> Rawdata.성별.value_counts()
1.0    29059
2.0    26689
Name: 성별, dtype: int64
  • value_counts(): 시리즈로 빈도표를 출력한다
  • 1은 남자, 2는 여자이므로, 총 55,748명 중 남자가 29,059명, 여자가 26,689명임을 알 수 있다.
  • 비율을 추가해보자.
성별_빈도표 = pd.DataFrame(Rawdata.성별.value_counts())
성별_빈도표.columns = ["freq"]
성별_빈도표["ratio"] = np.round(성별_빈도표.freq/sum(성별_빈도표.freq),2)
성별_빈도표
  • pd.DataFrame.columns: DataFrame의 컬럼명을 조작한다.
  • np.round(array, n): array의 값들을 n의 자리에서 반올림한다.

  • 대한민국의 중, 고등학생을 모집단으로 하는 데이터의 표본집단에서 남성이 차지하는 비중은 52%, 여성이 차지하는 비중은 48% 임을 쉽게 알 수 있다.

 

1.3. 양적 변수 도수분포표 만들기

  • 이번엔 동일한 방법으로 양적 변수인 키에 대해 도수분포표를 만들어보겠다.
키_빈도표 = pd.DataFrame(Rawdata.키.value_counts())
키_빈도표.columns = ["freq"]
키_빈도표["ratio"] = np.round(키_빈도표.freq/sum(키_빈도표.freq),2)
키_빈도표

  • 데이터를 보기 쉽게 만드려고 했는데, 여전히 보기가 어렵다.
  • 키와 같은 양적 변수는 들어갈 수 있는 값이 매우 다양하여 등급(Class)이 많기 때문에 이를 바로 도수분포표로 만들면, 여전히 보기 어렵다. 이를 범주화(Categorization)시켜 단순화해보자.
>>> 키_array = Rawdata.키.to_numpy()
>>> print("min:", 키_array.min())
>>> print("max:", 키_array.max())
min: 136.0
max: 196.0
  • DataFrame.column.to_numpy(): DataFrame의 특정 열 column을 numpy의 array로 변환시킨다.
  • array.min(): 배열의 최솟값
  • array.max(): 배열의 최댓값
  • 최솟값이 136.0cm, 최댓값이 196.0cm이므로, "140 이하", "140~150", "150~160", "160~170", "170~180", "180~190", "190 이상"으로 데이터를 범주화해보자.
키_범주 = np.where(키_array<=140, "140 이하",
                np.where((키_array>140) & (키_array<=150), "140~150",
                         np.where((키_array>150) & (키_array<=160), "150~160",
                                  np.where((키_array>160) & (키_array<=170), "160~170",
                                           np.where((키_array>170) & (키_array<=180), "170~180",
                                                    np.where((키_array>180) & (키_array<=190), "180~190", "190 이상"))))))
                                                    
키_도수분포표 = pd.DataFrame(pd.Series(키_범주).value_counts(), columns=["freq"])
키_도수분포표.sort_index(inplace=True)
키_도수분포표
  • np.where(조건, a, b): 조건에 해당하는 경우 a로 해당하지 않는 경우 b를 반환
  • DataFrame.sort_index(): index로 정렬함.

  • 비율을 추가해보자.
키_도수분포표["ratio"] = np.round(키_도수분포표.freq / sum(키_도수분포표.freq), 2)
키_도수분포표

 

1.4. 누적 빈도, 누적 비율 추가

  • 이번에는 빈도(freq)와 비율(ratio)을 첫 집단부터 차근차근 누적시키는 누적 빈도(Cumulative frequency)와 누적 비율(Cumulative ratio)을 보자.
키_도수분포표["cum_freq"] = np.cumsum(키_도수분포표.freq)
키_도수분포표["cum_ratio"] = np.cumsum(키_도수분포표.ratio)
키_도수분포표

  • np.cumsum(array): array를 주어진 순서대로 누적합

 

 

 

 

2. 도수분포표의 개념 정리

 위에서 만든 도수분포표를 기반으로, 도수분포표의 개념을 정리해 보자.

  • 질적 변수는 일반적으로 구성하고 있는 등급(Class)의 수가 그리 많지 않기 때문에 바로 도수분포표를 생성해도 가시성이 높다. 물론, 질적 변수 역시 구성하고 있는 등급(Class)의 수가 매우 많다면, 재 범주화하여, 그 등급(Class)의 수를 줄여야한다.
    예) 읍, 면, 동 단위로 지역명이 들어가 있는 변수는, 이를 시 단위로 재범주화 할 수 있음
  • 양적 변수로 구성된 데이터는 중복을 제거하더라도, 구성하고 있는 등급(Class)가 매우 많아 도수분포표로 만들더라도, 가시성이 매우 떨어지므로, 변수의 성격에 따라 그 변수의 데이터 분포를 가장 잘 보여줄 수 있는 구간으로 데이터를 범주화시켜 도수분포표를 만든다.

 

  • 각 등급(Class)에서 관찰된 객체의 수를 빈도수 또는 도수(Frequency)라고 하며, $f$로 나타낸다.
  • 각 변수를 구성하는 데이터의 빈도, 비율 등을 파악하기 때문에 이 과정을 빈도 분석(Frequency Analysis)라 하며, 출력된 도수분포표를 빈도표라고도 한다.
  • 빈도 분석을 통해 생성하는 도수분포표 자체도 통계 분석에서 목적이 될 정도로 중요하지만, 일반적으로는 데이터의 형태를 파악하는 기초 자료로 생성하는 경우가 많다.

 

  • 누적 빈도(Cumulative frequency):
    어떤 등급(Class)에 해당하는 빈도를 포함해, 그 이하 또는 그 이상에 있는 모든 빈도를 합친 것
  • 누적 빈도를 사용하게 되면, 특정 등급(Class)에 있는 대상이 전체 데이터에서 차지하는 위치를 알 수 있다. 위 도수분포표를 보면, 중·고등학생 전체 집단에서 키가 170~180에 있는 집단이 전체 대상에서 96%에 위치하고 있음을 알 수 있다.
  • 누적 빈도는 질적 변수에 대해서는 사용하지 않는 것을 추천한다. 양적 변수는 데이터에 순서가 있고, 값과 값 사이의 간격이 일정하므로, 누적 빈도를 통해 대상 등급(Class)의 객관적인 위치를 알 수 있다. 그러나, 명목 변수는 순서가 없고, 순서가 존재하는 서열 변수라 할지라도, 간격이 일정하지 않기 때문에, 대상 등급(Class)이 전체 등급의 어디에 위치하는지 알 수 없다.

 

 

 

 

3. 상대 빈도(Relative frequency) = 비율(Ratio)

  • 위에서 생성한 키에 대한 도수분포표는 표본 집단인 중·고등학교에 재학 중인 청소년을 대상으로 한 것이다.
>>> print("만연령 min:", Rawdata.연령.min())
>>> print("만연령 max:", Rawdata.연령.max())
만연령 min: 12.0
만연령 max: 18.0
  • 대상 집단의 연령 범위는 만 12세~18세이며, 성별이 남, 녀 두 가지가 들어 있기 때문에 단순하게 위 도수분포표를 보고, 해당 집단의 특성을 파악한다면 아래와 같은 정보 전달의 오류가 발생할 위험이 있다.
  • 키 170cm는 12세 여성이란 집단에서는 굉장히 큰 키이지만, 18세 남성이란 집단에서는 그리 큰 키가 아니다. 위 도수분포표는 만 12세 ~ 18세 중·고등학생 표본집단을 대상으로 하였기 때문에 위 도수분포표만으로는 둘을 같게 볼 여지가 있다.
    (물론, 대상을 만 12~18세라고 미리 설명해놨다면, 이런 문제는 발생하지 않는다.)
  • 보다 정밀한 데이터 파악을 위해선, 연구자의 의도를 가장 잘 보여줄 수 있는 도수분포표(빈도표) 생성이 필요하다.
  • 이번에는 만 16세인 사람으로 한정하여, 남성과 여성의 키를 보도록 하자.
def cat_height(array):
    cat_array = np.where(array<=140, "140 이하",
                     np.where((array>140) & (array<=150), "140~150",
                              np.where((array>150) & (array<=160), "150~160",
                                       np.where((array>160) & (array<=170), "160~170",
                                                np.where((array>170) & (array<=180), "170~180",
                                                         np.where((array>180) & (array<=190), "180~190", "190 이상"))))))
    return cat_array
    
    
def Freq_table(array):

    freq_table = pd.DataFrame(pd.Series(array).value_counts(), columns=["freq"])
    freq_table.sort_index(inplace = True)
    freq_table["ratio"] = freq_table.freq / sum(freq_table.freq)
    freq_table["cum_freq"] = np.cumsum(freq_table.freq)
    freq_table["cum_ratio"] = np.round(np.cumsum(freq_table.ratio), 2)

    # 반올림 및 총합 생성
    freq_table.loc["총합"] = [sum(freq_table.freq), sum(freq_table.ratio), "", ""]
    freq_table["ratio"] = np.round(freq_table["ratio"], 2)

    return freq_table
  • 위에서 만들었던, 양적 변수의 범주화(키)와 도수분포표를 출력하는 코드들을 정리하여 별 개의 함수로 만들어놓았으며, 추가로 총합이 계산되도록 코드를 추가하였다.
  • 이러한 코드 함수화를 통해, 코드의 재활용성, 가시성, 유지보수의 용이함 등의 이점을 얻을 수 있다.
남자_16세_키 = Rawdata[(Rawdata["연령"] == 16) & (Rawdata["성별"] == 1.0)]["키"].to_numpy()
여자_16세_키 = Rawdata[(Rawdata["연령"] == 16) & (Rawdata["성별"] == 2.0)]["키"].to_numpy()
Freq_table(cat_height(남자_16세_키))

Freq_table(cat_height(여자_16세_키))

  • 상대 빈도(relative frequency)는 비율(ratio)과 동일하다.
  • 상대 빈도 즉, 비율을 사용하면, 서로 다른 집단에 대하여, 각 범주별 차지하는 비중을 알 수 있게 되므로, 서로 다른 집단을 비교하는데 매우 유용하다.
  • 위 표를 하나로 합쳐보자.
# 성별에 따른 변수명 구분을 위해 column 앞에 특정 문자를 붙여줌
M_DF = Freq_table(cat_height(남자_16세_키))
M_DF = M_DF.add_prefix("M_")
F_DF = Freq_table(cat_height(여자_16세_키))
F_DF = F_DF.add_prefix("F_")

# 병합 및, 결측값은 0으로 채운다.
T_DF = pd.concat([M_DF, F_DF], axis=1).sort_index()
T_DF.fillna(0, inplace=True)

T_DF

  • 위 표를 보면, 남성(M)과 여성(F)의 도수분포표를 비율(ratio)을 이용해서, 두 집단의 규모 차이를 무시하고 비교할 수 있다.
  • 만 16세 남성의 59%는 170~180cm에 존재하며, 만 16세 여성의 50%는 160~170cm에 존재한다는 것을 알 수 있다.

 

 

 

 지금까지 기술통계학의 가장 기초가 되는 빈도 분석(Frequency analysis)의 도수분포표(Frequency distribution table)에 대해 알아보았다.

 빈도 분석은 모든 데이터 분석의 기반이 되며, 데이터의 분포를 파악하고, 연구자의 의도를 대상에게 전달하는 데 있어, 작성하기도 쉽고, 이해하기도 쉬우므로, 강력한 영향을 미친다.

 변수의 수가 무수히 많고, 하나하나의 변수를 구성하는 집단 역시 매우 많기 때문에, 모든 빈도를 보여주는 것보다, 연구자의 의도가 가장 잘 담겨 있는 대상에 대한 도수분포표를 보여주는 것이 매우 중요하며, 도수분포표를 생성하기 전에 자신이 전달하고자 하는 바가 무엇인지? 자신이 전달하고자 하는 바에서 대상 집단이 어떻게 되는지를 명확히 하도록 하자.

728x90
반응형

'Python으로 하는 기초통계학 > 기본 개념' 카테고리의 다른 글

중심경향치(1) - 최빈값, 중앙값  (0) 2021.03.03
도수분포표와 시각화  (0) 2021.03.02
통계 분석을 위한 데이터 준비  (0) 2021.03.01
변수(Variable)  (0) 2021.03.01
통계학이란?  (0) 2021.02.26
728x90
반응형

 DataFrame을 가지고 노려면, 기본적으로 Index와 Column 그리고 내가 원하는 값을 가지고 오는 법을 알아야 한다. 이전 포스트에서 Index를 가지고 노는 법에 대해 알아보았으니, 이번 포스트에서는 Column을 가지고 노는 방법에 대해 알아보도록 하겠다.

 

 

데이터 프레임과 칼럼(Column)

 이전 포스트에서 알아보았던, Index는 내가 원하는 Row(행, 레코드, 튜플)를 가지고 놀 수 있는 방법이었고, 이번에 학습할 Column은 열(변수, 속성, 필드)을 가지고 놀 수 있는 방법이다.

 DataFrame은 각 Column마다 담겨 있는 데이터의 양은 같으나, 담을 수 있는 데이터가 다르므로, 칼럼을 잘 가지고 놀 수 있어야만, DataFrame을 조작하기 쉬워진다.

 

 

 

 

1. 데이터 프레임의 기본 칼럼

  • 이전 "Pandas-데이터프레임 만들기"에서는 칼럼의 이름을 다 지정해줬었지만, 만약 데이터 프레임의 칼럼을 지정해주지 않는다면 어떻게 될까?
# Import Module
import pandas as pd
import numpy as np
# 기본 데이터 만들기
>>> np.random.seed(1234)
>>> matrix = np.random.randint(10, 100, size = (5,4))
>>> matrix
array([[57, 93, 48, 63],
       [86, 34, 25, 59],
       [33, 36, 40, 53],
       [40, 36, 68, 79],
       [90, 83, 57, 60]])
  • numpy의 array를 이용해서 단순한 행렬을 만들어보았다.
  • np.random.seed(): seed를 정해서 고정된 난수가 출력되도록 한다. 자신이 원하는 아무 숫자를 안에 넣고 실행 시, 그 seed에 대해 출력되는 난수는 동일한 값만 나오게 된다.
  • np.random.randint(low, high, size, dtype): low에서 high까지 자신이 지정한 size(shape)로 랜덤한 값이 출력된다.
>>> DF = pd.DataFrame(matrix)
>>> DF

  • 칼럼을 지정하지 않고 DataFrame을 만들면 Index와 마찬가지로 0부터 시작하는 값이 매겨진다.

 

1.1. RangeIndex

  • 이렇게 0부터 시작하는 index가 매겨지는 것을 RangeIndex라고 하는데, Python의 기본 함수인 range랑 그 기능이 거의 비슷하다.
# RangeIndex 생성
>>> pd.RangeIndex(10)
RangeIndex(start=0, stop=10, step=1)

# RangeIndex의 값을 list로 바꿔주자.
>>> list(pd.RangeIndex(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# range와 비교
>>> range(10)
range(0, 10)

>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • RangeIndex 역시 range처럼 특정 간격을 가지고 Data를 생성할 수 있으며, 객체의 값을 활성화해주지 않으면, 그 내용물이 튀어나오지 않는다.
# 시작, 끝, 간격을 조정해보자.
>>> pd.RangeIndex(start=10, stop = 30, step=2)
RangeIndex(start=10, stop=30, step=2)

>>> list(pd.RangeIndex(start=10, stop = 30, step=2))
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]

# 위 기능은 range와 같다.
>>> range(10, 30, 2)
range(10, 30, 2)

>>> list(range(10, 30, 2))
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
  • index는 많은 양의 Data를 담는 DataFrame 특성 상, 그 숫자가 매우 커질 수 있기 때문에 range와 마찬가지로 이를 바로 출력하지 않고, 호출 시 출력한다.
  • RangeIndex는 크게 신경 쓸 것은 아니지만, DataFrame을 조작하다가 칼럼의 이름을 지정할 수 없는 경우가 있다. 이럴 때는 RangeIndex가 출력되므로, 이를 조작할 수는 있어야 한다.

 

 

 

 

2. Column의 이름 바꾸기

  • DataFrame의 Column이름을 바꾸는 방법은 크게 두 가지가 있다.
  1. 한 번에 다 바꾸기
  2. 내가 원하는 Column의 이름만 바꾸기

 

2.1. Column의 이름 한 번에 다 바꾸기

>>> DF.columns = ["C1", "C2", "C3", "C4"]
>>> DF

>>> DF.columns
Index(['C1', 'C2', 'C3', 'C4'], dtype='object')

>>> type(DF.columns)
pandas.core.indexes.base.Index
  • DataFrame.columns: DataFrame의 Column을 조작하는 가장 기본적인 방법으로 DataFrame의 Column을 pandas index라는 type으로 가지고 온다. array와 유사하므로, array라고 생각해도 큰 문제는 없다.

 

2.2. 특정 Column의 이름만 바꾸기

  • 칼럼 C1은 math로 C3는 English로 바꿔보자.
>>> DF.rename(columns={"C1":"math", "C3":"English"}, inplace = True)
>>> DF

  • DataFrame.rename(columns={"old":"new"}, inplace=False): DataFrame의 old 컬럼을 new 컬럼으로 이름을 바꾼다.
  • 이번에는 C2를 math로 바꿔보자.
>>> DF.rename(columns={"C2":"math"}, inplace = True)
>>> DF

  • DataFrame의 컬럼 역시 Index처럼 이름이 중복된다.
  • 뒤에서 다시 다루겠지만, Index처럼 같은 이름의 컬럼 출력 시, 동일한 컬럼이 생성된다.
>>> DF.math

 

 

 

 

3. Column이름들을 꾸며주기

  • DataFrame의 컬럼의 이름은 위에서 봤듯이 중복될 수도 있고, DataFrame.columns라는 함수를 통해, array를 다루듯 조작할 수도 있다.
  • 또한, Column의 이름들에 특정한 패턴으로 한 번에 조작을 해줄 수도 있다.
  • 해당 예시를 보기 전에 조작이 쉽도록, 변수들의 이름을 전부 바꿔주었다.
DF.columns = ["mid math", "mid English", "fin math", "fin English"]

 

3.1. 컬럼명의 특정 문자 바꿔주기

>>> DF.columns = DF.columns.str.replace(" ", "_")
>>> DF

  • DataFrame.columns.str.replace(pat, repl): 판다스의 str모듈은 문자열 데이터를 다루는데 특화된 함수의 모음으로, 보통 칼럼 각각에 적용하지만, 칼럼의 이름에도 적용 가능하다.
  • 컬럼을 다룰 때, 공백이 있는 경우 조작이 조금 불편해지므로, 가능한 공백은 언더바("_")로 치환하도록 하자.
  • str 모듈은 굉장히 유용한 함수들을 모아놓은 모듈이므로, 추후 자세히 다루도록 하겠다.

 

3.2. 모든 컬럼명 앞에 공통 문자 붙이기

>>> DF.add_prefix("A__")

 

3.3. 모든 컬럼명 뒤에 공통 문자 붙이기

>>> DF.add_suffix("_Dummy")

  • 컬럼명의 앞이나 뒤에 공통된 문자를 붙여주는 기능은 데이터 전처리 시, 더미 변수를 만들거나, One-Hot Vector 등을 만들 때, 쉽게 쓸 수 있다.
  • 지금처럼 모든 변수들에게 특정 문자를 붙이는 것보다는, 특정 컬럼들만 선택하여, 공통 문자를 바꾸는 것이 더 효과적이다. 이는 추후 컬럼 조회를 학습하면서 공부하도록 하겠다.

 

 

 

 지금까지 컬럼명을 가지고 노는 방법에 대해 학습해 보았다. 다음 포스트에서는 컬럼명을 가지고 놀 수 있는 다른 방법에 대해 학습해보도록 하겠다.

728x90
반응형
728x90
반응형

 이전 포스트에서 DataFrame의 생김새와 부위별 이름에 대해 알아보았다. DataFrame의 가장 큰 특징은 index가 존재한다는 것이고, 이 index를 얼마나 잘 가지고 노느냐에 따라 DataFrame을 얼마나 효율적으로 사용할 수 있는지가 나뉘게 된다.

 

 

데이터 프레임과 인덱스

 Pandas에 대해 흔히들 가지고 있는 오해가 Pandas는 순차적으로 데이터를 조회하기 때문에, 데이터 전처리 속도가 매우 느리다는 것이다.

 이 말은 반은 맞고, 반은 틀리다고 할 수 있는데, DataFrame에서 특정 데이터를 단순하게 조회하면, 순서대로 하나하나 조회하기 때문에 속도가 매우 느려지고, 도리어 이 특징을 이용해서, 데이터 전처리 속도를 줄일 수도 있다.

 그러나, 인덱스를 사용하여 조회를 하게 된다면, 순차적 조회가 아닌 한 번에 index에 해당하는 값을 가지고 오기 때문에 조회 속도가 엄청 빨라진다.

 그러므로, Pandas를 사용해야 하는 데이터 분석가라면 반드시 이 인덱스를 잘 가지고 놀 수 있어야 한다.

 

 

 

 

1. DataFrame 생성 시, 원하는 Index 부여하기

  • index는 DataFrame을 생성하면서 부여할 수도 있고, DataFrame을 생성한 후에도 바꿔줄 수 있다.
  • 다음과 같은 데이터 셋을 만들어보자.
import pandas as pd
name_list = ["박명수", "유재석", "노홍철", "길", "정준하", "정형돈", "하하"]
math_list = [65, 95, 70, 80, 100, 85, 60]
english_list = [75, 80, 85, 90, 65, 75, 100]
class_list = [1, 2, 1, 2, 2, 2, 1]
index_list = list(range(10, 24, 2))

exam = pd.DataFrame({"name":name_list, "math":math_list, "english":english_list,
                     "class":class_list}, index=index_list)
exam

  • range(start, end, by): start부터 end까지 값을 by 간격으로 만들 준비를 한다.
    (list 함수 같은 것으로 가동해주지 않으면, 대기 상태를 유지한다)
  • 생성된 데이터 프레임을 보듯, 데이터 프레임 생성 시, index 파라미터에 넣어준 list를 index로 부여한다.

 

 

 

 

2. 내가 원하는 칼럼을 index로 부여하기

  • 이미 index가 정해졌다 할지라도, index는 내가 원하는 칼럼을 index로 만들어줄 수 있다.
exam.set_index("name", inplace=True)
exam

  • DataFrame.set_index(column, drop=True, append=False, inplace=False): set_index()는 말 그대로, 내가 원하는 칼럼을 index로 설정해주는 함수다.
  • drop 파라미터는 기존 칼럼을 index로 넣을 때, 기존 칼럼을 말 그대로 버려버릴지를 묻는 것이다.
  • append 파라미터는 기존 인덱스에 내가 원하는 칼럼까지 추가해서 인덱스를 만들지를 묻는 것이다.
  • inplace 파라미터는 원본 데이터에 덮어씌울지를 물어보는 것이다. 여기서는 inplace = True를 하였으므로, index가 바뀐 것을 바로 원본에 적용하였다.
  • 중복되는 값이라 할지라도 index로 만들 수 있다.
exam.set_index("class", inplace=True)
exam

  • 데이터 프레임의 인덱스에 대해 "중복이 돼서는 안 되는 유니크한 값이어야 한다."라 오해를 하는 경우가 종종 있는데, 위에서 보시다시피 중복이 가능하다. 이는, 데이터 프레임의 인덱스의 특징이며, 이 특징을 잘 이용하는 것이 중요하다.
  • 데이터 프레임의 인덱스는 중복 가능하므로, 조회 시, 해당 인덱스에 해당하는 값을 모두 가지고 온다.
exam.loc[2]

  • DataFrame.loc[index]: index에 해당하는 행을 조회하는 방법으로 추후 자세히 다루도록 하겠다.

 

 

 

 

3. 인덱스 초기화

  • 무한도전 멤버들의 이름을 인덱스로 했었다가 class로 인덱스를 설정하니, 무한도전 멤버들의 이름이 사라져 버렸다.
  • 이는 DataFrame.set_index()는 기본적으로 기존 index를 없애는 것을 전재로 하기 때문이다.
  • 이러한 일을 막기 위해선 set_index()를 하기 전에 인덱스를 초기화해줘야 한다.
exam.reset_index(drop = False, inplace = True)
exam

  • DataFrame.reset_index(drop=False, inplace=False): reset_index는 인덱스를 말 그대로 초기화시켜버린다.
  • drop 파라미터는 기존의 index를 버릴지 말지를 결정하는 파라미터다
  • 무한도전 멤버들의 이름이 index인 상태에서 reset_index()하고 set_index()를 하였다면, 이번처럼 기존 인덱스가 날아가버리는 일을 막을 수 있다.

 

 

 

 

4. 새로운 list를 인덱스로 부여하고 인덱스를 조작해보자

  • 처음에 만들었던, name_list를 index로 부여하여, 무한도전 멤버의 이름이 index인 상태로 돌아가 보자.
exam.index = name_list
exam

  • DataFrame.index: DataFrame의 index를 조작할 수 있다.
  • 이전에는 index의 이름이 name으로 나왔으나, index는 본래 이름이 따로 없기 때문에 index의 이름이 없는 상태로 출력된 것을 볼 수 있다.

4.1. 인덱스 이름 부여

exam.index.name = "name"
exam

  • DataFrame.index는 뒤에 다른 함수들을 붙여 추가적인 기능들을 실행할 수 있다.
  • 이번에는 DataFrame.index.name으로 하여 index의 이름을 부여해보았다.

4.2. 인덱스 데이터 추출

>>> exam.index
Index(['박명수', '유재석', '노홍철', '길', '정준하', '정형돈', '하하'], dtype='object', name='name')
  • list를 index에 넣었듯, array로 index를 뽑아낼 수도 있다.
  • 이러한 index 조작은 데이터 프레임을 다룰 때, 상당히 쓸모 있는 기술이므로 꼭 숙지하도록 하자.

 

 

 

 

5. 멀티 인덱스(Multi index)와 인덱스를 기준으로 정렬

  • 멀티 인덱스는 말 그대로 index를 한 번에 여러 개 사용하는 방법이다.
  • 무한도전 멤버의 이름을 칼럼으로 되돌리고, class, name으로 멀티 인덱싱을 해보자.
exam.reset_index(drop=False, inplace=True)
exam.set_index(["class", "name"], inplace = True)
exam

  • 멀티 인덱스는 set_index(column)에서 칼럼을 여러 개 넣어서 실시할 수 있다.
  • 멀티 인덱스를 하고 나니, class 2의 길, 정준하, 정형돈의 class가 하나로 붙어 깔끔하게 나온 것을 볼 수 있다.
  • 그러나, 썩 깔끔해 보이지 않으므로 인덱스를 기준으로 정렬시켜보자.

5.1. 인덱스 기준 정렬

exam.sort_index(inplace=True)
exam

  • DataFrame.sort_index(ascending=True, inplace=False): 말 그대로 index순으로 정렬(sort)한다.
  • ascending 파라미터는 오름차순, 내림차순을 의미하며, 기본적으로 오름차순 정렬이 실행된다.

5.2. 멀티 인덱스의 실제 모습

  • 멀티 인덱스를 실행하게 되면, 데이터의 형태를 보다 쉽게 파악할 수 있다는 장점이 있다. 때문에 멀티 인덱스는 group_by를 하여 두 집단 이상의 기술 통계량을 볼 때, 멀티 인덱스로 출력되게 된다.
  • 멀티 인덱스를 하면, class는 하나의 index로 뭉친 것처럼 보이는데, 눈에만 저렇게 보이지 실제로는 따로 떨어져 있다.
>>> exam.index
MultiIndex([(1, '노홍철'),
            (1, '박명수'),
            (1,  '하하'),
            (2,   '길'),
            (2, '유재석'),
            (2, '정준하'),
            (2, '정형돈')],
           names=['class', 'name'])
  • exam의 index를 보면, 튜플로 묶여 있는 것을 볼 수 있다.

5.3. 멀티 인덱스 해제

exam.reset_index(drop=False)

  • 멀티 인덱스 해제는 reset_index()를 하여 쉽게 할 수 있다.

 

 

 

 지금까지 판다스에서 인덱스를 가지고 노는 법에 대해 학습해보았다. 판다스에서 인덱스를 얼마나 잘 활용하느냐에 따라 판다스의 속도는 크게 변하므로, 인덱스를 잘 활용하길 바란다.

728x90
반응형
728x90
반응형

 이전 포스트에서 데이터 프레임을 만들어보았으니, 이번에는 데이터 프레임의 부위별 이름을 알아보자.

 

 

데이터 프레임의 구조와 용어 정리

  • 위 표는 우리가 지금까지 봐온 데이터 프레임이다. 데이터 프레임의 생김새는 우리가 자주 봐왔던 표(Table)처럼 생겼다.
  • Data를 사용하는 분야에서 기본적으로 사용하는 관계형 데이터베이스(Relational Database, RDM)의 테이블(Table)도 데이터 프레임과 동일하게 생겼다.
  • RDB를 여기저기서 많이 사용하다 보니, 그 용어를 데이터 프레임에도 사용하는 경우가 많기 때문에, RDB에서 사용하는 용어도 함께 정리하도록 하겠다.

 

 

 

 

1. 칼럼(Column)

  • 칼럼(Column)은 단어 그대로 을 의미하며, 행과 열을 가진 데이터에서 열은 모두 칼럼이라고 불린다.
  • 변수(Variable), 속성(Attribute), 필드(Field)라고도 불린다.
  • 데이터에서 객체(대상)가 가지고 있는 특징이 들어간다.
  • 예를 들어, 이름, 성별, 나이와 같은 대상 집단이 가지고 있는 특징이다.
  • 차수(Degree): RDB에서는 칼럼의 수를 가리킨다.
    예) 위 테이블의 Degree는 11이다.
  • 도메인(Domain): RDB에서 한 칼럼에서 가질 수 있는 값의 집합
    예) 성별의 도메인은 "남", "여" 둘 뿐이며, 그 외의 값은 들어갈 수 없다.

 

 

 

 

2. 로우(Row)

  • 말 그대로 행을 가리키며, 데이터 하나하나를 의미한다. 그러다 보니, 모양이 데이터 프레임과 조금 다르더라도, 데이터 하나하나를 지칭할 때, 단순하게 Row라고 부르기도 한다.
  • 튜플(Tuple), 레코드(Record)라고도 불린다.
  • 데이터의 대상이 되는 객체 하나하나를 의미한다.
  • 예를 들어, 위 데이터 프레임에서 index가 417번인 사람은, 고객번호(PassengerId)가 1309번이고, 이름이 Peter, Master. Michael J이며, 성별이 male인 사람이다.
  • 카디널리티(Cardinality): 행의 총 개수이다.

 

 

 

 

3. 인덱스(Index)

  • 데이터 프레임만의 특징으로, index는 중복될 수도 있고, 동일한 index에 해당하는 row의 값이 다를 수도 있다.
  • index를 어떻게 조작하느냐에 따라, 데이터 프레임에서의 데이터 조회 속도 차이가 엄청 커진다.
  • RDB의 key와 유사해 보이지만, RDB의 key는 고유 개체를 식별할 수 있는 유니크한 값임에 반해, 데이터 프레임의 index는 중복이 가능하고, index가 동일하나, row에 있는 값은 다를 수 있으므로, key와 굉장히 다르다.

 

 

 

 지금까지 데이터 프레임의 행, 열의 다른 용어와 그것이 의미하는 바에 대해 간단히 다뤄봤다. 위 단어 외에도 인스턴스(Instance), 엔티티(Entity)와 같은 다양한 단어가 있으나, 데이터 프레임이던 테이블이던 기본적으로 행과 열이 중심이기 때문에 위 용어만 알면, 데이터에 대한 기본적인 의사소통은 할 수 있을 것이다.

 다음 포스트에서는 index를 가지고 노는 방법에 대해 학습해보도록 하겠다.

728x90
반응형
728x90
반응형

 이전 포스트에서 판다스(Pandas)가 무엇인지 간단한 소개와 판다스의 대표적인 데이터 타입인 데이터 프레임(DataFrame)과 시리즈(Series)를 살펴보았다.

 이번 포스트는 판다스의 상징인 데이터 프레임을 만드는 방법에 대해 알아보도록 하겠다.

 

 

데이터 프레임 만들기

1. 판다스 가져오기

  • 판다스를 사용하기 앞서 먼저 판다스를 설치하고 판다스를 사용하겠다고 선언해보자.
  • Python 모듈 설치는 크게 Anaconda를 사용하여 일반적으로 사용하는 Pandas를 포함한 각종 라이브러리를 한 번에 다운로드하는 방법이 있고, pip를 이용해서 다운로드하는 방법이 있다.
  • 해당 방법을 서술하기엔 그 내용이 꽤 길고 헷갈리기 쉽기 때문에 Pandas를 비롯한 각종 라이브러리를 설치하는 방법이 적힌 이전 포스트로 가는 참조를 걸어놓겠다.
  • 참고 1 - 라이브러리 설치 방법: "Python 필요한 모듈들을 설치해보자"
  • 참고 2 - Anaconda 설치 방법: "Python 파이썬과 아나콘다"
  • 참고 3 - 오프라인 환경에서 라이브러리 설치 방법: "Python 오프라인 환경에서 파이썬 패키지를 설치해보자"
  • 위 방법대로 Pandas를 정상적으로 설치하였다는 전제하에 Pandas에 대해 학습해보도록 하겠다.
# Import module
import pandas as pd
  • Python에서 #은 주석을 만드는 특수 문자로 코드(Syntax) 앞에 입력 시, 해당 행의 명령어들을 주석 처리한다.
  • import pandas as pd는 문자 그대로 "import(수입하다) pandas as(처럼) pd"로, pandas를 가지고 오는데 pd라는 문자로 가지고 온다는 의미이다.
  • Python은 특정 라이브러리 내 함수를 사용하려면 "라이브러리.함수()" 이런 식으로 코드를 짜게 되는데, pandas는 제법 긴 단어이므로 pd라는 간결한 단어로 라이브러리를 의미하겠다는 소리다.
  • 일반적으로 pandas는 pd라 사용하니 pd로 쓰도록 하자.

 

 

 

 

2. 데이터 프레임 함수 설명

  • pandas에서 제공하는 DataFrame API에서 DataFrame이 가지고 있는 파라미터(Parameter)는 다음과 같다.
  • pd.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
  • data: list나 numpy에서 제공하는 array, dictionary 등을 받는다. 간단하게 생각해서 행렬이나 벡터를 Data로 사용할 수 있다고 생각하면 된다.
  • index: 이전 포스트에서 말했듯, pandas의 데이터 프레임이나 시리즈의 특징은 index를 갖는다는 것이다. 그리고 사용자가 원하는 index를 부여할 수 있다. 지정하지 않은 경우, 자동으로 range(0, len(data))에 해당하는 index가 생성된다.
  • columns: 열, 즉 변수의 이름이다.
  • dtype: DataFrame 안에 들어갈 데이터의 type을 결정한다. DataFrame의 장점은 변수 별로 다른 dtype이 들어갈 수 있는 것이므로, 특수한 경우가 아닌 이상 지정 안 한다.
  • copy: DataFrame의 특징 중 하나인, Data의 종속성에 대한 부분인데, 이는 나중에 다루도록 하겠다. 크게 신경 쓰지 않아도 된다.

 

 

 

 

3. 행렬(2-d array)을 사용해서 데이터 프레임 만들기

  • 2-d array는 2차원 배열을 의미하며, 우리에게 친숙한 행렬이 2-d array다.
  • 머신러닝 라이브러리인 sklearn에서 제공하는 붓꽃 데이터를 이용해서 행렬을 보도록 하자.
import numpy as np
from sklearn.datasets import load_iris
  • numpy는 선형 대수학에 특화된 라이브러리로 다른 포스팅에서 자세히 다룰 테니 일단 넘어가자(당장 쓰지 않더라도 Pandas를 쓸 때는 꼭 numpy도 import 해놓는 습관을 들여놓자. 나중에 아주 많이 쓰게 될 것이다.)
  • from sklearn.datasets import load_iris는 sklearn의 dataset이라는 모듈에서 load_iris라는 함수만 가져오겠다는 뜻이다. 특정 라이브러리에서 한, 두 개의 함수만 필요한 경우에 사용하는 방법이다.
>>> iris = load_iris()
>>> iris
{'data': array([[5.1, 3.5, 1.4, 0.2],
        [4.9, 3. , 1.4, 0.2],
        [4.7, 3.2, 1.3, 0.2],
        [4.6, 3.1, 1.5, 0.2],
        [5. , 3.6, 1.4, 0.2],
        [5.4, 3.9, 1.7, 0.4],
        
...

lustering system finds 3 classes in the data.\n   - Many, many more ...',
 'feature_names': ['sepal length (cm)',
  'sepal width (cm)',
  'petal length (cm)',
  'petal width (cm)'],
 'filename': 'C:\\Users\\gooop\\anaconda3\\lib\\site-packages\\sklearn\\datasets\\data\\iris.csv'}
  • 해당 블로그에서 >>> 는 코드 실행을 의미하며, 앞에 >>>가 없는 것은 출력된 결과를 의미한다. 한 셀 안에 >>>가 없는 경우, 출력된 결과가 없기 때문에 >>>를 쓰지 않은 것이다.
  • load_iris()를 실행 시, dictionary가 출력된다. dictionary는 여러 종류의 데이터를 key:value로 담을 수 있는 사전이다.
  • dictionary를 key로 조회 시, 원하는 value만 볼 수 있다.
# iris data에 있는 key를 보자
>>> iris.keys()
dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])
  • iris 데이터가 가지고 있는 key를 보면, data, target, frame 등 여러 key가 존재하는 것을 알 수 있다.
  • 여기서 "data"는 iris data를 의미한다.
  • "feature_names"는 변수의 이름이다.
>>> iris["data"]
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       [5. , 3.6, 1.4, 0.2],
       
...

       [6.7, 3. , 5.2, 2.3],
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]])
  • 데이터가 매우 크므로 중간을 생략하였다. 위 데이터 같이 행과 열로 2개의 차원(dimension)이 존재하는 데이터를 2차원 배열 2-d array라고 한다.
>>> iris["feature_names"]
['sepal length (cm)',
 'sepal width (cm)',
 'petal length (cm)',
 'petal width (cm)']
  • 칼럼 이름은 list로 출력되었다.
  • 위 두 데이터를 사용해서 데이터 프레임을 생성해보자.
>>> iris_DF = pd.DataFrame(data=iris["data"], columns=iris["feature_names"])

  • DataFrame은 주피터 노트북 기준 한 번에 60개의 행이 조회되며, 아래에 있는 150 rows x 4 columns는 150개의 행과 4개의 열을 가진 데이터라는 것을 의미한다.
>>> iris_DF.values
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       [5. , 3.6, 1.4, 0.2],
       
...

       [6.7, 3. , 5.2, 2.3],
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]])
  • DataFrame.values 함수를 사용하면, DataFrame을 2-d array로 돌릴 수 있다. 꽤 중요한 기능이니 꼭 숙지하도록 하자.

 

 

 

 

4. list를 사용해서 데이터 프레임 만들기

  • 이번엔 list를 이용해서 데이터 프레임을 만들어보자.
  • list는 Python에서 대표적인 Data를 담는 그릇이다.
>>> name_list = ["박명수", "유재석", "노홍철", "길", "정준하", "정형돈", "하하"]
>>> math_list = [65, 95, 70, 80, 100, 85, 60]
>>> english_list = [75, 80, 85, 90, 65, 75, 100]
>>> exam = pd.DataFrame({"name":name_list, "math":math_list, "english":english_list})
>>> exam

  • 데이터 프레임은 위 결과처럼 각 변수(열) 별로 다른 dtype이 들어갈 수 있다.

 

 

 

 

5. Dictionary를 사용해서 데이터 프레임 만들기

  • Dictionary는 모든 형태의 데이터를 Key:value의 형태로 담을 수 있는 데이터 타입이다.
  • Dictionary의 형태는 조금만 편집해서 바로 Json으로 사용할 수 있을 정도로 Json과 굉장히 유사하다.
  • Dictionary의 value들이 모두 list 또는 array이고, 그 길이가 동일하다면, DataFrame으로 만들 수 있다.
    (길이가 다르다면, 오류가 뜬다)
>>> Raw_dict = {
>>>     "name":["박명수", "유재석", "노홍철", "길", "정준하", "정형돈", "하하"],
>>>             "math":[65, 95, 70, 80, 100, 85, 60],
>>>             "english":[75, 80, 85, 90, 65, 75, 100],
>>>             "class":[1, 2, 1, 2, 2, 2, 1]
>>> }

>>> Raw_dict

{'name': ['박명수', '유재석', '노홍철', '길', '정준하', '정형돈', '하하'],
 'math': [65, 95, 70, 80, 100, 85, 60],
 'english': [75, 80, 85, 90, 65, 75, 100],
 'class': [1, 2, 1, 2, 2, 2, 1]}
DF = pd.DataFrame(Raw_dict)
DF

  • Dictionary는 key와 value로 이루어져 있으므로, 별도의 컬럼 이름을 지정해주지 않아도 DataFrame으로 쉽게 전환이 가능하다.
  • 반대로 DataFrame을 Dictionary로 변환하는 것 역시 굉장히 간단하다.
>>> DF.to_dict()
{'name': {0: '박명수', 1: '유재석', 2: '노홍철', 3: '길', 4: '정준하', 5: '정형돈', 6: '하하'},
 'math': {0: 65, 1: 95, 2: 70, 3: 80, 4: 100, 5: 85, 6: 60},
 'english': {0: 75, 1: 80, 2: 85, 3: 90, 4: 65, 5: 75, 6: 100},
 'class': {0: 1, 1: 2, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1}}
  • DataFrame.to_dict(): 해당 함수를 사용하면 DataFrame을 dictionary로 바로 전환할 수 있다.

 

 

 

 지금까지 DataFrame을 만드는 방법에 대해 알아보았다. 다음 포스트에서는 데이터 프레임의 부위별 이름에 대해 알아보도록 하겠다.

728x90
반응형
728x90
반응형

판다스(Pandas) 소개

 파이썬을 처음 사용하는 데이터 분석가가 제일 먼저 공부해야 할 라이브러리를 한 가지 꼽으라면, 많은 사람들이 판다스(Pandas)를 선택할 것이다.

 판다스는 R과 마찬가지로 데이터 프레임(DataFrame)을 사용해서, 데이터를 시각화, 분석을 할 수 있는데, R의 데이터 프레임이 그렇듯 매우 직관적이고, 데이터를 가지고 놀기 좋은 R의 기능을 대부분 사용할 수 있기 때문에 데이터 분석가에게 있어 필수 라이브러리라고 할 수 있다.

 

 사족으로 판다스라고 하면, 동물인 판다가 먼저 떠오를 텐데, 판다스는 동물에서 따온 이름이 아닌, 계량 경제학에서 사용하는 "패널 데이터(Panner Data)"에서 따온 이름이다.

 사회 과학에서 자주 다뤄지는 패널 데이터를 간략히 설명하자면, 횡단 데이터인 한 시점에서의 데이터 셋이 종단 데이터로 규칙적인 기간을 간격으로 여러 개 존재하는 데이터를 말한다. 즉, 종단 + 횡단의 성격을 갖는 데이터가 패널 데이터다. 이는 판다스가 한 시점에서 뿐만이 아닌 시계열 데이터에도 강한 면모를 가진다는 뜻이기도 하다.

 

 판다스는 대용량 데이터를 다룰 때나, 서비스를 위해 0.5초, 1초 내의 빠른 연산이 필요한 상황에선 취약한 모습을 보이기 때문에 만능이라고 할 수는 없으나, 판다스는 데이터의 흐름이나 데이터의 특징 파악이 매우 쉬우므로, 먼저 판다스로 코드를 짜고, 속도가 매우 빠른 Numpy로 코드를 수정하면, 이를 쉽게 해결할 수 있다.

 

 

 

 

1. 판다스의 데이터 타입

  • 판다스는 크게 2개의 고유 데이터 타입을 가지고 있다.
  • 하나는 데이터 프레임(DataFrame)이고, 다른 하나는 시리즈(Series)이다.
  • 어떻게 생겼는지만 간략히 봐보자.
import pandas as pd
from sklearn.datasets import load_iris

# 붓꽃(iris) 데이터를 가져와보자.
iris_dict = load_iris()

DF = pd.DataFrame(iris_dict["data"], columns=iris_dict["feature_names"])
DF

  • 위 표가 데이터 프레임이다.
  • 데이터 프레임은 마치 액셀처럼 이쁘게 표로 나눠져 있는 형태다.
  • 엑셀과 차이라면, 데이터 프레임은 모든 데이터를 한 번에 보여주지 않는다는 것이다.
  • 데이터 프레임은 한 번에 최대 60개 행까지 보여준다.
>>> DF[:60]

>>> DF["sepal length (cm)"]
0      5.1
1      4.9
2      4.7
3      4.6
4      5.0
      ... 
145    6.7
146    6.3
147    6.5
148    6.2
149    5.9
Name: sepal length (cm), Length: 150, dtype: float64
  • 시리즈는 array와 굉장히 비슷하며, 데이터 프레임은 길이가 동일한 시리즈가 열의 숫자만큼 붙어있다고 생각해도 좋다.
  • array와의 차이점은 array의 index는 무조건 0부터 시작하지만, 시리즈는 그렇지 않다는 것이다. 또한 데이터 프레임이나 시리즈는 멀티 인덱스라 하여, 2개의 칼럼을 인덱스로 사용할 수 있다.

 

 

 

 다음 포스트에서는 본격적으로 데이터 프레임을 다뤄보도록 하겠다.

728x90
반응형

+ Recent posts