pandas의 고유한 자료구조 - Series와 DataFrame 이해하기

pandas에서는 고유하게 정의한 자료 구조인 Series와 DataFrame을 사용하여, 빅 데이터 분석에 있어 높은 수준의 성능을 발휘합니다. Series는 동일한 데이터형의 복수 개의 성분으로 구성된 자료 구조이며, DataFrame은 서로 같거나 다른 데이터형의 여러 개의 열에 대하여 복수 개의 성분으로 구성된 '표와 같은 형태'의 자료 구조입니다.

Series

Series는 pd.Series() 함수를 사용하여 정의합니다. 이 때 Python 리스트나, 혹은 numpy array 등이 함수의 인자로 입력됩니다.

Series는 각 성분의 인덱스와, 이에 대응되는 값으로 구성되어 있습니다. Series 생성 시 인덱스를 따로 명시해주지 않으면, 0으로 시작하는 정수 형태의 기본 인덱스가 부여됩니다. 기본 인덱스 대신 Series 생성 시 각 성분에 대한 인덱스를 사용자가 직접 명시할 수도 있습니다.

obj = pd.Series([4, 7, -5, 3])
obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])

어떤 Series obj의 인덱스만을 추출하고 싶을 경우 obj.index를, 값만을 추출하고 싶을 경우 obj.values를 사용하면 됩니다. 그리고 obj.dtype을 사용하면, 해당 Series에 부여된 데이터형을 확인할 수 있습니다.

Series 자체의 이름과 Series의 인덱스에 대한 이름을 지정해줄 수 있습니다. 각각 obj.nameobj.index.name에 값을 대입해 주면 됩니다.

DataFrame

DataFrame은 pd.DataFrame() 함수를 사용하여 정의합니다. DataFrame에 입력할 데이터는 Python 딕셔너리 혹은 numpy의 2차원 array 등의 형태가 될 수 있습니다.

data = {"names": ["Kilho", "Kilho", "Kilho", "Charles", "Charles"],
        "year": [2014, 2015, 2016, 2015, 2016],
        "points": [1.5, 1.7, 3.6, 2.4, 2.9]}
df = pd.DataFrame(data)

DataFrame에서는 서로 다른 두 종류의 인덱스가 각각 행 방향과 열 방향에 부여되어 있으며, 이들이 교차하는 지점에 실제 값이 위치해 있는 것을 확인할 수 있습니다. 이렇게 DataFrame에는 두 종류의 인덱스가 있으며, 이들을 구분하기 위해 특별히 행 방향의 인덱스를 '인덱스', 열 방향의 인덱스를 '컬럼'이라고 부릅니다. DataFrame df에 대하여, df.indexdf.columns를 사용하여 인덱스와 컬럼을 각각 확인할 수 있습니다. 그리고 df.values를 사용하면 DataFrame의 값에 해당하는 부분만 2차원 array의 형태로 얻을 수 있습니다.

DataFrame의 인덱스와 컬럼에도 Series와 유사한 방식으로 이름을 지정해줄 수 있습니다. 각각 df.index.namedf.columns.name에 값을 대입해 주면 됩니다.

* DataFrame 관련 함수 혹은 변수 등을 조회하고자 할 경우

어떤 특정한 DataFrame df와 관련하여 실행할 수 있는 함수 혹은 변수의 정확한 이름이 제대로 기억나지 않을 경우, IPython Notebook에서 df.까지만 입력하고 TAB 키를 누르면 사용 가능한 모든 변수 및 함수가 표시됩니다.

* NaN (not a number)

NaN은 numpy나 python에서 'not a number'를 표시하는 약어로, 특정 위치에 값이 존재하지 않을 때 이를 표시하기 위한 기호라고 할 수 있습니다. 여러분이 데이터셋 파일을 DataFrame의 형태로 읽어들이는 과정에서, 만약 특정 부분의 값이 포함되어 있지 않았을 경우 해당 위치에 이렇게 NaN으로 표시됩니다.

NaN으로 표시된 부분에 대해서는 추후에 어떠한 연산도 수행할 수 없기 때문에, 추후에 이 부분을 실제 값으로 메꿀 방법을 생각해 보아야 합니다.

describe 함수

DataFrame df에 대하여 df.describe() 함수를 실행하게 되면, 계산이 가능한 컬럼에 한해서 각 컬럼의 평균, 분산, 최솟/최댓값 등 기본 통계량을 산출한 결과를 보여줍니다. 이는 데이터셋을 DataFrame 형태로 막 읽어들인 직후에, 데이터셋을 전체적으로 살펴보고자 할 때 사용하기 유용합니다.

DataFrame 인덱싱 이해하기

기본 인덱싱

df라는 이름의 DataFrame을 다음과 같이 정의합니다.

data = {"names": ["Kilho", "Kilho", "Kilho", "Charles", "Charles"],
           "year": [2014, 2015, 2016, 2015, 2016],
           "points": [1.5, 1.7, 3.6, 2.4, 2.9]}
df = pd.DataFrame(data, columns=["year", "names", "points", "penalty"],
                          index=["one", "two", "three", "four", "five"])

열 선택하고 조작하기

df에서 'year' 열만을 얻고 싶을 경우, df["year"]를 실행합니다. 그러면 'year' 열의 값들이 Series의 형태로 인덱스와 함께 표시됩니다. 하나의 열을 가져오는 경우 df.year와 같이 해도 동일한 결과를 얻습니다. 두 가지 방법 중에서 여러분이 편한 것을 사용하시면 됩니다.

만약 복수 개의 열을 가져오고자 할 경우에는, df[["year", "points"]]와 같은 형태로 실행하시면 됩니다. 이 때, 중괄호 안에 컬럼 이름으로 구성된 리스트가 들어간다는 점을 주의하시길 바랍니다.

특정 열을 이렇게 선택한 뒤, 값을 일괄적으로 대입해줄 수도 있습니다. df["penalty"] = 0.5 또는 df["penalty"] = [0.1, 0.2, 0.3, 0.4, 0.5]를 실행하면, 기존에 NaN으로 표시되었던 'penalty' 열에 지정한 값을 대입하게 됩니다. 새로운 'zeros'라는 이름의 열을 추가하고자 할 경우, df["zeros"] = np.arange(5)와 같이 실행하시면 됩니다.

pandas의 Series를 DataFrame의 새로운 열의 값으로 대입할 수도 있는데, 이 경우 해당 Series에서 명시된 인덱스와 DataFrame 상의 인덱스를 비교하여 대응되는 인덱스의 값을 대입하는 형태로 이루어집니다. 이것이 열의 값을 Python 리스트나 numpy array로 입력하는 경우와, pandas Series로 입력하는 경우의 핵심적인 차이입니다.

val = pd.Series([-1.2, -1.5, -1.7], index=["two", "four", "five"])
df["debt"] = val

새로운 열의 값을 기존 열의 값을 사용하여 입력할 수도 있습니다. 예를 들어 'net_points'라는 열의 값이 'points'에서 'penalty'를 뺀 것이라고 합시다. 그리고 'high_points' 열은 'net_points'의 값이 2.0보다 클 때 True, 그렇지 않을 때 False를 가지도록 합시다. 다음과 같이 실행하면 됩니다.

    df["net_points"] = df["points"] - df["penalty"]
    df["high_points"] = df["net_points"] > 2.0

기존의 열을 DataFrame 상에서 삭제해 버리는 것은 아주 간단합니다. del 키워드와 함께 삭제할 열을 명시하면 됩니다.

del high_points
del net_points
del zeros

행 선택하고 조작하기

pandas DataFrame의 행을 인덱싱할 수 있는 방법은 무수하게 많습니다. 그리고 열을 인덱싱하는 방법에 있어서도 바로 위에서 소개했던 방법은 여러 가지 중 하나에 불과합니다. 더욱 머리를 아프게 하는 것은, pandas의 버전이 올라가면서 이러한 인덱싱 방법도 미묘하게 달라지고 있다는 것입니다.

다른 기능들과의 혼동을 방지하기 위해, DataFrame의 행을 선택하고자 할 때, 저는 .loc이나 .iloc을 사용하시길 권장드리고자 합니다.

df.loc

.loc은, 실제 인덱스를 사용하여 행을 가져올 때 사용합니다. 예를 들어 df.loc["two"]를 실행하면, 인덱스가 'two'인 행의 값을 모두 가져옵니다. df.loc["two":"four"]을 실행하면, 인덱스가 'two'부터 'four'까지의 범위에 있는 행의 모든 값을 가져옵니다.

.loc을 사용하면, 인덱스 뿐만 아니라 컬럼 역시 동시에 명시할 수 있습니다. 예를 들어 'two'부터 'four'까지 인덱스의 값들 중 'points' 열의 값만을 가져오고자 할 경우, df.loc["two":"four", "points"]를 실행하면 됩니다. 이 경우에도 하나의 열을 가져왔으므로 결과물은 Series가 됩니다. df.loc["three":"five", "year":"penalty"]와 같이 실행하면, 인덱스와 컬럼 모두 범위 인덱싱이 가능합니다.

새로운 행을 추가하고자 할 경우, df.loc["six", :] = [2013, "Hayoung", 4.0, 0.1, 2.1]과 같이 .loc을 사용하여 새로운 인덱스의 행을 선택하고, 여기에 대입할 값을 리스트 혹은 array의 형태로 입력해주면 됩니다.

df.iloc

.iloc은, numpy의 array 인덱싱 방식으로 행을 가져올 때 사용합니다. 예를 들어 df.iloc[3]을 실행하면, 인덱스 3에 해당하는 4행을 모두 가져옵니다. df.iloc[3:5, 0:2]와 같이 행과 열에 대한 범위 인덱싱이 가능하며, df.iloc[[0, 1, 3], [1, 2]]와 같이 원하는 인덱스만을 명시하여 가져올 수도 있습니다.

DataFrame의 행을 선택하는 방법은 위에서 소개해 드린 방법 외에도 몇 가지가 더 있습니다. 그럼에도 불구하고, 여러분들께 .loc.iloc만을 사용하여 행을 선택하고 조작하시길 권장드립니다. 그래야 나중에 혼란이 적을 것입니다.

불리언 인덱싱

pandas의 DataFrame에서도 열이나 행을 선택할 때, 불리언 인덱싱이 가능합니다.

df에서, 'year' 열의 값이 2014보다 큰 행만을 선택하고 싶다고 가정합시다. df["year"] > 2014를 실행하면, 값이 2014보다 큰 위치에는 True, 그 외에는 False가 들어가 있는 불리언 Series를 얻게 됩니다. 이를 마스크라고 부릅니다.

이런 마스크는 DataFrame을 인덱싱하는 데 사용될 수 있습니다. df.loc[df["year"] > 2014, :]를 실행하면, 앞서 확인했던 마스크가 True에 해당하는 행만으로 구성된 DataFrame을 얻을 수 있습니다. df.loc[df["names"] == "Kilho", ["names", "points"]]를 실행하면, 'names' 열의 값이 "Kilho"인 행의 값 중에서 'names'와 'points' 열에 해당하는 값들만을 얻을 수 있습니다.

만약 사용할 조건, 즉 마스크가 여러 개 필요하다면, df.loc[(df["points"] > 2) & (df["points"] < 3), :]와 같이 실행하면 됩니다. 조건과 조건 간에 '&'가 들어가 있는 것을 주목하시길 바랍니다. '&'(and) 연산자를 사용하는 경우 두 개의 마스크가 동시에 True인 경우에만 해당 인덱스를 조회하며, '|'(or) 연산자를 사용하는 경우 두 개의 마스크 중 최소 하나가 True인 경우에 해당 인덱스를 조회합니다.

DataFrame 이리저리 조작하기

결측값 또는 이상치 처리하기

여러분이 pandas를 사용하여 읽어들인 데이터셋 파일에 NaN의 형태로 빠진 값이 존재하거나, 혹은 정상 범주에서 벗어난 값이 포함되어 있는 경우가 얼마든지 발생할 수 있습니다. 이러한 값들을 각각 결측값(missing value)이상치(outlier)라고 하는데, 본격적인 데이터 분석에 앞서 이들을 적절히 처리할 필요가 있습니다.

df = pd.DataFrame(np.random.randn(6, 4))
df.columns = ["A", "B", "C", "D"]
df.index = pd.date_range("20160701", periods=6)

랜덤한 숫자들로 구성된 DataFrame df를 정의하고, 인덱스와 컬럼을 명시하였습니다. 이 때, pandas에서 제공하는 pd.date_range() 함수를 사용하였는데, 이는 일자와 시각을 나타내는 데이터형인 datetime으로 구성된 인덱스를 생성할 때 사용하는 함수입니다. 여러분들이 많이 접할 데이터셋 중 하나가 바로 시계열(time series) 형태의 데이터셋인데, pandas는 이렇게 datetime과 관련된 간편한 기능을 많이 제공하고 있습니다.

df의 'F' 열을 새로 정의하고, 여기에 다음과 같이 값을 대입합니다.

df["F"] = [1.0, np.nan, 3.5, 6.1, np.nan, 7.0]

NaN을 인위적으로 사용하고자 할 때, numpy에서 제공하는 np.nan을 사용합니다.

df.dropna(how="any")를 실행하면, 각각의 행을 살펴보면서 행 내 값들 중에 NaN이 하나라도 포함되어 있는 경우 해당 행을 DataFrame 상에서 삭제합니다. 반면 df.dropna(how="all")을 실행할 경우, 행 내 값들 모두가 NaN인 경우 해당 행을 DataFrame 상에서 삭제합니다.

아니면 df의 NaN을 실제 수치값으로 대체하고 싶을 수 있습니다. 이러한 경우 df.fillna(value=5.0)와 같이 실행하면, NaN을 지정한 값으로 대체한 결과물을 얻을 수 있습니다.

한편, df.isnull() 함수를 실행하면 DataFrame 상에서 NaN이 포함되어 있는 위치에 대한 불리언 마스크를 얻을 수 있습니다. 이를 응용하여 df.loc[df.isnull()["F"], :]를 실행하면, 'F'열에 NaN을 포함하고 있는 행만을 선택할 수 있습니다.

다음으로 각 행의 값을 관찰하여, 이상치가 포함되어 있는 행을 DataFrame 상에서 직접 삭제하는 방법을 알아보도록 하겠습니다. 예를 들어 2016년 7월 1일 행 데이터를 선택하고자 할 때, pd.to_datetime("20160701")을 실행하면 "20160701"이라는 문자열을 datetime 데이터형으로 변환하며, 이를 인덱스로 사용할 수 있습니다.

df.drop(pd.to_datetime("20160701"))을 실행한 결과를 보면, 2016년 7월 1일 행 데이터가 DataFrame 상에서 삭제된 것을 확인할 수 있습니다. 이렇게 인덱스를 기준으로 특정 행을 삭제하고자 할 경우 .drop() 함수를 사용하면 됩니다. 두 개 이상의 인덱스를 기준으로 복수 개의 행을 삭제하고자 할 경우, df.drop([pd.to_datetime("20160702"), pd.to_datetime("20160704")])와 같이 .drop() 함수의 매개변수로 datetime 리스트를 입력하면 됩니다.

.drop() 함수를 사용하면, 행 뿐만 아니라 열도 삭제할 수 있습니다. df.drop("F", axis=1)와 같이 삭제할 컬럼을 명시한 후 axis=1 매개변수를 명시해 주면, 'F' 열이 삭제된 것을 확인할 수 있습니다. 이 경우에도 df.drop(["B", "F"], axis=1)의 형태로 복수 개의 열을 명시할 수도 있습니다.

DataFrame 데이터 분석용 함수 사용하기

통계 함수

pandas의 DataFrame에서는 다양한 통계 함수를 지원합니다. DataFrame을 사용하여 데이터 분석을 할 때 가장 중요한 기능이라고 할 수 있겠습니다.

data = [[1.4, np.nan],
           [7.1, -4.5],
        [np.nan, np.nan],
        [0.75, -1.3]]
df = pd.DataFrame(data, columns=["one", "two"], index=["a", "b", "c", "d"])

df라는 DataFrame을 정의하여, '행 방향' 또는 '열 방향'의 합을 구하도록 할 수 있습니다. df.sum(axis=0)을 실행하면 '행 방향'으로 성분들의 합을 구하게 되며, df.sum(axis=1)을 실행하면 '열 방향'으로 성분들의 합을 구하게 됩니다. 이렇게 별다른 옵션 지정 없이 .sum() 함수를 그냥 호출할 경우, 기본적으로 NaN은 배제하고 합을 계산합니다.

특정 행 또는 특정 열에 대해서만 합을 계산할 수도 있습니다. df["one"].sum()을 실행하면 'one' 열의 합만을 얻을 수 있으며, df.loc["b"].sum()을 실행하면 'b' 행의 합만을 얻을 수 있습니다.

평균이나 분산 등의 경우 사용하는 함수만 달라질 뿐, 방식은 완전히 동일합니다. 각각 .mean().var() 함수를 사용하면 됩니다.

만약 NaN을 모두 감안해서 통계량을 산출하고 싶다면, 통계 함수에 skipna=False라는 인자를 추가로 입력하면 됩니다. 예를 들어 df.mean(axis=1, skipna=False)를 실행하고 그 결과를 관찰해 보면, NaN이 포함된 행은 평균값이 제대로 계산되지 않고 NaN이라고 표시되어 있는 것을 확인할 수 있습니다.

pandas의 DataFrame에 적용되는 통계 함수 정리
함수 설명
count 전체 성분의 (NaN이 아닌) 값의 갯수를 계산
min, max 전체 성분의 최솟, 최댓값을 계산
argmin, argmax 전체 성분의 최솟값, 최댓값이 위치한 (정수)인덱스를 반환
idxmin, idxmax 전체 인덱스 중 최솟값, 최댓값을 반환
quantile 전체 성분의 특정 사분위수에 해당하는 값을 반환 (0~1 사이)
sum 전체 성분의 합을 계산
mean 전체 성분의 평균을 계산
median 전체 성분의 중간값을 반환
mad 전체 성분의 평균값으로부터의 절대 편차(absolute deviation)의 평균을 계산
std, var 전체 성분의 표준편차, 분산을 계산
cumsum 맨 첫 번째 성분부터 각 성분까지의 누적합을 계산 (0에서부터 계속 더해짐)
cumprod 맨 첫번째 성분부터 각 성분까지의 누적곱을 계산 (1에서부터 계속 곱해짐)

이들 통계 함수를 사용하여 DataFrame의 결측값을 채울 수도 있습니다. 예를 들어 'one' 열의 NaN은 나머지 값들의 평균값으로, 'two' 열의 NaN은 해당 열의 최솟값으로 대체하고자 한다고 가정합시다. 다음과 같이 실행하면, 'one' 열과 'two' 열의 결측값들이 각각 의도했던 대로 채워질 것입니다.

one_mean = df.mean(axis=0)["one"]
two_min = df.min(axis=0)["two"]
df["one"] = df["one"].fillna(value=one_mean)
df["two"] = df["two"].fillna(value=two_min)

DataFrame에서 특히 유용한 통계 함수 중 하나가, 상관 계수(correlation coefficient) 혹은 공분산(covariance)을 계산하는 함수입니다. 새로운 df2 DataFrame을 정의합니다.

df2 = pd.DataFrame(np.random.randn(6, 4),
                   columns=["A", "B", "C", "D"],
                   index=pd.date_range("20160701", periods=6))

df2에서 'A' 열의 데이터와 'B' 열의 데이터 간의 상관계수를 산출하고자 할 경우, df2["A"].corr(df2["B"])을 실행하면 됩니다. 같은 방식으로, 'B'열 데이터와 'C'열 데이터 간의 공분산을 산출하고자 할 경우 df2["B"].cov(df2["C"])을 실행하면 됩니다.

혹은 특정한 두 개의 열을 명시할 필요 없이, df2.corr() 또는 df2.cov()를 실행하면 df2의 모든 열들 간의 상관계수 및 공분산을 한 번에 계산해 줍니다. 데이터 분석 시 어느 한 변수가 다른 변수에 미치는 영향 등을 알고자 할 때, 이러한 상관 분석이 대단히 유용할 수 있습니다.

정렬 함수 및 기타 함수

정렬 함수

pandas의 DataFrame에서는 인덱스 기준 정렬과 값 기준 정렬을 지원합니다. 기존 df2의 인덱스와 컬럼 순서를 무작위로 섞어보도록 하겠습니다.

dates = df2.index
random_dates = np.random.permutation(dates)
df2 = df2.reindex(index=random_dates, columns=["D", "B", "C", "A"])

df2를 확인하면, 인덱스와 컬럼의 순서가 불규칙하게 변화한 것을 확인할 수 있습니다. 실제로 시계열 데이터같은 경우에도 이렇게 시간에 따라 제대로 정렬되어 있지 않은 데이터셋을 얻을 가능성이 존재합니다.

이 때 인덱스가 오름차순이 되도록 행들을 정렬하고 싶은 경우, df2.sort_index(axis=0)을 실행하여 '행 방향'으로의 정렬을 수행합니다. 만약 컬럼이 오름차순이 되도록 열들을 정렬하고 싶은 경우, df2.sort_index(axis=1)을 실행하여 '열 방향'으로의 정렬을 수행합니다.

만약 인덱스가 내림차순이 되도록 행들을 정렬하고 싶은 경우, df2.sort_index(axis=0, ascending=False)와 같이 ascending=False 인자를 추가로 입력해 주면 됩니다.

다음으로 값 기준 정렬입니다. 예를 들어 'D'열의 값이 오름차순이 되도록 행들을 정렬하고 싶은 경우, df2.sort_values(by="D")를 실행합니다. 만약 'B'열의 값이 내림차순이 되도록 행들을 정렬하고 싶은 경우, df2.sort_values(by="B", ascending=False)를 실행하면 됩니다.

df2["E"] = np.random.randint(0, 6, size=6)
df2["F"] = ["alpha", "beta", "gamma", "gamma", "alpha", "gamma"]

df2에 'E'열과 'F'열을 추가한 뒤, 'E' 열과 'F' 열의 값을 동시에 기준으로 삼아 각각 오름차순이 되도록 행들을 정렬하고 싶은 경우, df2.sort_values(by=["E", "F"])와 같이 여러 개의 컬럼을 리스트 형태로 명시해 주면 됩니다.

기타 함수

df.unique() 함수는 지정한 행 또는 열에서 중복을 제거한 유니크한 값들만을 제시하는 함수입니다.

uniques = df2["F"].unique()

df.value_counts() 함수는 특정한 행 또는 열에서 값에 따른 갯수를 제시하는 함수입니다.

df2["F"].value_counts()

df.isin() 함수를 사용하면, 특정한 행 또는 열의 각 성분이 특정한 값들을 포함되어 있는지 확인할 수 있으며, 불리언 마스크를 반환합니다. 그래서 이렇게 얻은 불리언 마스크를 사용하여, 원하는 행만을 선택할 수도 있습니다.

df2["F"].isin(["alpha", "beta"])
df2.loc[df2["F"].isin(["alpha", "beta"]), :]

사용자 정의 함수

사용자가 직접 정의한 함수를 DataFrame의 행 또는 열에 적용하는 방법에 대해 알아보도록 하겠습니다. 새로운 df3 DataFrame을 먼저 정의합니다.

df3 = pd.DataFrame(np.random.randn(4, 3), columns=["b", "d", "e"],
                   index=["Seoul", "Incheon", "Busan", "Daegu"])

lambda 함수를 정의한 뒤, 이를 func 변수에 저장합니다. 이 때 func 함수는, 해당 행 또는 열에서의 최댓값 - 최솟값을 계산하는 함수입니다.

func = lambda x: x.max() - x.min()

df.apply() 함수를 사용하여 해당 함수 인자로 func를 명시하면, 해당 DataFrame의 각 행 또는 열에 대하여 func 함수를 적용하고 그 결과값을 얻을 수 있습니다. 예를 들어 df3.apply(func, axis=0)을 실행하면 이를 '행 방향'으로 진행한 결과를, df3.apply(func, axis=1)을 실행하면 이를 '열 방향'으로 진행한 결과를 얻습니다.

pandas를 사용한 데이터 분석 맛보기

파일 읽기

[Lending Club Loan 데이터셋 다운로드]

본 강의에서는, Lending Club Loan dataset 2007-2015를 사용합니다.

대부분의 데이터셋은 열과 열을 구분하기 위한 구분자로 특정한 문자를 사용합니다. 구분자는 보통 콤마 ','를 많이 쓰는데, 콤마를 구분자로 사용한 파일을 특별히 'CSV(comma-separated values) 파일'이라고 부릅니다.

CSV 파일은 pd.read_csv() 함수를 사용하여 손쉽게 읽어들일 수 있습니다. 해당 함수를 호출할 때, 읽어들일 데이터 파일의 경로와 구분자 등을 입력합니다.

df = pd.read_csv("data/lending-club-loan-data/loan.csv", sep=",")

Lending Club Loan dataset 분석하기

Lending Club Loan dataset의 주요 컬럼 요약
  • loan_amnt: 대출자의 대출 총액
  • funded_amnt: 해당 대출을 위해 모금된 총액
  • issue_d: 대출을 위한 기금이 모금된 월
  • loan_status: 대출의 현재 상태*
  • title: 대출자에 의해 제공된 대출 항목
  • purpose: 대출자에 의해 제공된 대출 목적
  • emp_length: 대출자의 재직 기간
  • grade: LC assigned loan grade**
  • int_rate: 대출 이자율
  • term: 대출 상품의 기간 (36-month vs. 60-month)

* 불량 상태(bad status): "Charged Off", "Default", "Does not meet the credit policy. Status:Charged Off", "In Grace Period", "Default Receiver", "Late (16-30 days)", "Late (31-120 days)"

** LC loan grade 참고: https://www.lendingclub.com/public/rates-and-fees.action

필요한 열 발췌 및 결측값 제거하기

df2 = df[["loan_amnt", "loan_status", 
          "grade", "int_rate", "term"]]
df2 = df2.dropna(how="any")

'36개월 대출'과 '60개월 대출'의 대출 총액 파악

term_to_loan_amnt_dict = {}
uniq_terms = df2["term"].unique()
for term in uniq_terms:
    loan_amnt_sum = df2.loc[df2["term"] == term, "loan_amnt"].sum()
    term_to_loan_amnt_dict[term] = loan_amnt_sum
term_to_loan_amnt = pd.Series(term_to_loan_amnt_dict)

각 대출 상태(불량/우량)에 따른 대출 등급 분포 파악

total_status_category = df2["loan_status"].unique()
bad_status_category = total_status_category[[1, 3, 4, 5, 6, 8]]
df2["bad_loan_status"] = df2["loan_status"].isin(bad_status_category)
bad_loan_status_to_grades = \
    df2.loc[df2["bad_loan_status"] == True, "grade"].value_counts()
bad_loan_status_to_grades.sort_index()

대출 총액과 대출 이자율 간의 상관관계 파악

df2["loan_amnt"].corr(df2["int_rate"])

파일 쓰기

여러분이 얻을 bad_loan_status_to_grades Series를 외부 파일로 쓰는 작업을 진행해보도록 합시다. 이는 DataFrame에 대해서도 동일하게 적용됩니다.

Series.to_csv() 혹은 df.to_csv() 함수를 사용하면, 현재 Series 혹은 DataFrame 형태로 들고 있는 분석 결과물을 외부 파일로 손쉽게 쓸 수 있습니다. 해당 함수를 호출할 때, 저장할 파일의 경로와 구분자 등을 인자로 같이 입력합니다.

bad_loan_status_to_grades.to_csv("bad_loan_status.csv", sep=",")

results matching ""

    No results matching ""