ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] pandas란 (series, dataframe, index 설명)
    언어/파이썬 & 장고 2022. 8. 21. 03:33

    pandas는 numpy를 기반으로 만들어진 새로운 패키지로 DataFrame이라는 효율적인 자료구조를 제공합니다. DataFrame은 행과 열 레이블이 부착된 다차원 배열로 여러가지 타입의 데이터를 가질 수 있고 데이터 누락도 허용하고 있습니다. pandas는 편리한 스토리지 인터페이스와 데이터베이스 프레임워크, 스프레드시트 프로그램 사용자에게 익숙한 데이터 연산을 구현합니다.

    데이터에 레이블을 붙이거나 누락딘 데이터로 작업하는 것과 같이 유연성이 필요한 작업과 요소 단위의 브로드캐스팅에 잘 매핑되지 않은 연산(그룹화, 피벗 등)을 하고자 하는 경우에는 numpy의 ndarray에서는 한계가 있습니다. pandas는 Series와 DataFrame 객체는 numpy 배열 구조를 기반으로 데이터 먼징(데이터를 정리하고 통합하는 과정) 작업을 효율적으로 할 수 있게 만듭니다.

    pandas 객체

    pandas 객체는 행과 열이 단순 정수형 인덱스가 아닌 레이블로 식별되는 numpy의 구조화된 배열을 보강한 버전이라고 볼 수 있습니다. pandas에서는 세 가지 기본 자료구조인 Series, DataFrame, Index가 존재합니다.

    Series

    import pandas as pd
    
    data = pd.Series([0.25, 0.5, 0.75, 1.0])
    
    print(data)
    # 0    0.25
    # 1    0.50
    # 2    0.75
    # 3    1.00
    # dtype: float64
    
    print(data.index)
    # RangeIndex(start=0, stop=4, step=1)
    
    print(data.values)
    # [0.25 0.5  0.75 1.  ]

    위 코드 예제와 같이 Series는 값과 인덱스로 구성이 되어 있습니다. 값은 numpy 배열이고 index는 지정하지 않으면 자동 채번되는 숫자라고 볼 수 있습니다.

    numpy 배열과 동일하게 인덱스로 접근할 수 있습니다.

    print(data[1])
    # 0.5
    
    print(data[1:3])
    # 1    0.50
    # 2    0.75
    # dtype: float64

    Series 객체가 1차원 numpy 배열과 호환될 것처럼 보이지만 인덱스 존재 여부라는 차이가 존재합니다. numpy 배열에는 값에 접근하는 데 사용되는 암묵적으로 정의된 정수형 인덱스가 있고 pandas의 Series에는 값에 연결된 명시적으로 정의된 인덱스가 있습니다.

    이러한 명시적인 인덱스 정의는 Series 객체에 추가적인 기능을 제공합니다. 예를 들어, 인덱스는 정수일 필요가 없습니다.

    import pandas as pd
    
    data = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])
    
    print(data)
    # a    0.25
    # b    0.50
    # c    0.75
    # d    1.00
    # dtype: float64
    
    print(data[0])
    # 0.25
    
    print(data[1:3])
    # b    0.50
    # c    0.75
    # dtype: float64
    
    print(data['a'])
    # 0.25
    
    print(data['a':'c'])
    # a    0.25
    # b    0.50
    # c    0.75
    # dtype: float64

    위의 예시처럼 기본 리스트에 접근하는 인덱스도 사용이 가능하고 사용자가 지정한 인덱스로도 지정할 수 있습니다. 슬라이싱의 경우, 기존 방식은 가장 마지막 인덱스는 포함하지 않지만 사용자 지정 인덱스는 포함하여 값을 반환합니다.

    이러한 특성으로 Series는 타입이 지정된 키를 일련의 타입이 지정된 값에 매핑하는 구조로 생각하면 Series를 파이썬 딕셔너리의 특수한 버전이라고 생각할 수도 있습니다. 타입이 지정된다는 것이 중요한데 특정 연산에서 numpy 배열 뒤의 타입 특정 컴파일된 코드가 그것을 파이썬 리스트보다 더 효율적으로 만들어주는 것처럼 pandas Series의 타입정보는 특정 연산에서 파이썬 딕셔너리보다 더 효율적입니다.

    다음은 Series 객체를 구성하는 다양한 예제입니다.

    import pandas as pd
    
    data = pd.Series(5, index=[100, 200, 300, 500])
    print(data)
    # 100    5
    # 200    5
    # 300    5
    # 500    5
    # dtype: int64
    
    data = pd.Series({2: 'a', 1: 'b', 3: 'c'})
    print(data)
    # 2    a
    # 1    b
    # 3    c
    # dtype: object
    
    data = pd.Series({2: 'a', 1: 'b', 3: 'c'}, index=[3, 2])
    print(data)
    # 3    c
    # 2    a
    # dtype: object

    DataFrame

    Series와 마찬가지로 DataFrame 또한 numpy 배열의 일반화된 버전이나 파이썬 딕셔너리의 특수한 버전으로 생각할 수 있습니다.

    Series가 유연한 인덱스를 가지는 1차원 배열이라면 DataFrame은 유연한 행 인덱스와 유연한 열 이름을 가진 2차원 배열이라고 볼 수 있습니다. 2차원 배열을 정렬된 1차원 열의 연속으로 볼 수 있듯이 DataFrame은 정렬된 Series 객체의 연속으로 볼 수 있습니다. 여기서 정렬은 같은 인덱스를 공유한다는 의미입니다.

    import pandas as pd
    
    population_dict = {
        'california': 333333333,
        'texas': 111111111,
        'new york': 222222222,
        'florida': 444444
    }
    
    area_dict = {
        'california': 86122,
        'texas': 21133,
        'new york': 91531,
        'florida': 77821
    }
    
    area = pd.Series(area_dict)
    population = pd.Series(population_dict)
    
    states = pd.DataFrame({'population': population, 'area': area})
    print(states)
    #             population   area
    # california   333333333  86122
    # texas        111111111  21133
    # new york     222222222  91531
    # florida         444444  77821
    
    print(states.index)
    # Index(['california', 'texas', 'new york', 'florida'], dtype='object')
    
    print(states.columns)
    # Index(['population', 'area'], dtype='object')

    위 예시처럼 2차원 객체를 구성할 수 있고 Series와 마찬가지로 index 속성을 가지며 열 레이블을 가지고 있는 Index 객체인 column 속성 또한 존재합니다.

    또한 Series와 마찬가지로 딕셔너리의 특수 버전으로도 볼 수 있습니다. DataFrame은 열 이름을 열 데이터로 이뤄진 Series에 매핑합니다.

    print(states['area'])
    # california    86122
    # texas         21133
    # new york      91531
    # florida       77821
    # Name: area, dtype: int64

    2차원 numpy 배열에서는 data[0]이 첫 번째 행을 반환하지만 DataFrame에서는 data[’col0’]이 첫 번째 열을 반환합니다.

    또한 다음과 같이 여러 방법으로 DataFrame을 구성할 수 있습니다.

    import numpy as np
    import pandas as pd
    
    # 딕셔너리의 리스트로 구성하기
    data = pd.DataFrame([{'a': 0, 'b': 0}, {'a': 1, 'b': 2}, {'a': 2, 'b': 4}])
    print(data)
    #    a  b
    # 0  0  0
    # 1  1  2
    # 2  2  4
    
    # 딕셔너리의 일부 키 누락 - NaN 은  (Not a Number)
    data = pd.DataFrame([{'a': 0, 'b': 2}, {'b': 1, 'c': 3}])
    print(data)
    #      a  b    c
    # 0  0.0  2  NaN
    # 1  NaN  1  3.0
    
    # 2차원 numpy 배열에서 구성하기
    data = pd.DataFrame(np.random.rand(3, 2), columns=['foo', 'bar'], index=['a', 'c', 'b'])
    print(data)
    #         foo       bar
    # a  0.678264  0.748772
    # c  0.778607  0.081286
    # b  0.630717  0.620151
    
    # 2차원 numpy 배열에서 구성하기에서 columns이나 index가 생량이 되면 각각 정수가 사용됨
    data = pd.DataFrame(np.random.rand(3, 2), columns=['foo', 'bar'])
    print(data)
    #         foo       bar
    # 0  0.056900  0.759261
    # 1  0.493867  0.638043
    # 2  0.991024  0.221578
    
    data = pd.DataFrame(np.random.rand(3, 2), index=['a', 'c', 'b'])
    print(data)
    #           0         1
    # a  0.493370  0.643671
    # c  0.577569  0.133437
    # b  0.189572  0.419588
    
    # numpy의 구조화된 배열에서 구성하기
    a = np.zeros(3, dtype=[('A', 'i8'), ('B', 'f8')])
    print(a)
    # [(0, 0.) (0, 0.) (0, 0.)]
    
    data = pd.DataFrame(a)
    print(data)
    #    A    B
    # 0  0  0.0
    # 1  0  0.0
    # 2  0  0.0

    Index

    위에서 Series와 Dataframe 객체는 데이터를 참조하고 수정하게 해주는 명시적인 인덱스를 포함하는 것을 알았습니다. Index 객체는 불변의 배열이나 정렬된 집합(Index 객체가 중복되는 값을 포함할 수 있으므로 기술적으로 중복집합)으로 볼 수 있습니다. 이 관점은 Index 객체에서 사용할 수 있는 연산에 몇 가지 흥미로운 결과를 가져옵니다.

    Index 객체는 배열과 비슷하게 동작하며 numpy 배열에서 익숙한 속성도 많이 있습니다.

    import pandas as pd
    
    index = pd.Index([2, 3, 5, 7, 11])
    print(index)
    # Int64Index([2, 3, 5, 7, 11], dtype='int64')
    
    print(index[1])
    # 3
    
    print(index[::2])
    # Int64Index([2, 5, 11], dtype='int64')
    
    print(index.size)
    # 5
    
    print(index.shape)
    # (5,)
    
    print(index.ndim)
    # 1
    
    print(index.dtype)
    # int64

    Index 객체와 numpy 배열의 한 가지 차이점이라면 Index 객체는 일반적인 방법으로 변경할 수 없는 불변의 값입니다.

    index = pd.Index([2, 3, 5, 7, 11])
    
    index[1] = 123
    
    # TypeError: Index does not support mutable operations

    이러한 불변성 덕분에 예기치 않은 인덱스 변경으로 인한 부작용 없이 여러 DataFrame과 배열 사이에서 인덱스를 안전하게 공유할 수 있습니다.

    pandas 객체는 집합 연산의 여러 측면에 의존하는 데이터세트 간의 조인과 같은 연산을 할 수 있게 하려고 고안됐습니다. Index 객체는 대체로 파이썬에 내장된 set 데이터 구조에서 사용하는 표기버을 따르기 때문에 합집합, 교집합, 차집합을 비롯해 그 밖의 조합들이 익숙한 방식으로 계산될 수 있습니다.

    댓글