매주 목요일 오후에는 자기주도 학습 시간에 학습한 내용을 적는 TIL을 작성하고, 링크를 제출하여야 한다고 한다!
시간이 부족해서 중간까지 밖에 못했지만 일단 정리하고 제출한 후 마저해야겠다.
근데 TIL이 이렇게 진행하는 게 맞는건지 모르겠다.
velog에 옮기는데 시간이 너무 걸리는거 같은데..?
안해봐서 어색해서 그렇겠지..!
python 공식 문서에 있는 초보자용 가이드!
코드와 간단한 설명이 나와있어, 해당 코드를 따라쳐보며 판다스에 대한 감을 잡아보기로 했다.
직접 jupyter에 쳐보며 실습하였고, 모르는 내용은 검색하며 정리하였다!
import numpy as np
import pandas as pd
numpy
와 pandas
를 함께 import 하여 사용한다.
pandas의 data structre 인 Series
와 DataFrame
을 만들어본다.
s = pd.Series([1,3,5,np.nan,6,8])
s
>>>
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
Series
는 index
와 data
를 가진 1차원 벡터형 자료형임을 알 수 있다.
dates = pd.date_range("20130101",periods = 6)
dates
>>>
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
Numpy array
를 전달하여 DataFrame
을 생성한다.
이때, date_range()
를 이용해 datetime index를 가진 df를 만든다.
pandas의 date_range()
는 날짜와 periods를 인자로 받아 해당 일자의 리스트를 만들어 주는 듯 하다.
df = pd.DataFrame(np.random.randn(6,4), index = dates, columns = list("ABCD"))
df
>>>
A B C D
2013-01-01 1.397870 1.781252 -0.181087 0.096765
2013-01-02 -0.797328 0.518531 -1.973643 -2.018436
2013-01-03 0.981060 0.731136 0.286162 -1.494021
2013-01-04 -0.293389 1.593434 1.016599 0.050886
2013-01-05 -0.242990 0.827032 -0.050350 -0.766830
2013-01-06 -0.587438 1.721439 0.258103 -0.264088
DataFrame을 만들었다. 전달해준 데이터는 np.random.randn(6,4)
로,
numpy의 random.randn
(가우시안 표준정규분포 생성)을 사용해 shape가 (6,4)
인 nparray를 만들었다.
df2 = pd.DataFrame({
"A" : 1.0,
"B" : pd.Timestamp("20130102"),
"C" : pd.Series(1, index = list(range(4)), dtype = "float32"),
"D" : np.array([3]*4, dtype = int),
"E" : pd.Categorical(["test","train","test","train"]),
"F" : "foo",
})
df2
>>>
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 1.0 3 train foo
2 1.0 2013-01-02 1.0 3 test foo
3 1.0 2013-01-02 1.0 3 train foo
앞에서는 행렬 형태의 nparray
를 전달해 DataFrame을 만들고, index
와 column
을 정해주었다면,
여기서는 직접 각 column에 들어갈 데이터들을 전달해 주어 df를 만들었다.
이때, df의 column에 들어갈 수 있는 여러 데이터 형식들로 series, nparray, float, str, Categorical
이 있었다.
Categorical
이란? 판다스에 존재하는 범주형 자료형.categories, codes
라는 속성을 가지며,
해당 속성을 이용하기 위해서는.cat
접근자(accessor)를 사용해 주어야 한다.
.set_categories()
등을 이용해 여러 카테고리의 이름을 지정해 줄 수 있다.
나중에 더 공부할 것 같으니 일단 여기까지!
아 그리고, 대체 foo가 뭐지..? 엥 홍길동 같이 의미 없는 문자열을 그냥 "foo"
라고 쓴단다.
df2.dtypes
>>>
A float64
B datetime64[ns]
C float32
D int32
E category
F object
dtype: object
.dtypes
의 메소드로 각각 column
들의 dtype을 알아볼 수 있다.
df.head()
>>>
A B C D
2013-01-01 1.397870 1.781252 -0.181087 0.096765
2013-01-02 -0.797328 0.518531 -1.973643 -2.018436
2013-01-03 0.981060 0.731136 0.286162 -1.494021
2013-01-04 -0.293389 1.593434 1.016599 0.050886
2013-01-05 -0.242990 0.827032 -0.050350 -0.766830
상위 및 하위의 자료들만 보기 위해서는 .head()
또는 .tail()
을 사용한다.
df.columns
>>>
Index(['A', 'B', 'C', 'D'], dtype='object')
df.index
또는 df.columns
를 이용해 index 와 column을 살펴볼 수 있다.
df.to_numpy()
>>>
array([[ 1.39786968, 1.78125156, -0.18108725, 0.09676474],
[-0.79732826, 0.5185311 , -1.97364305, -2.01843616],
[ 0.98105995, 0.73113614, 0.28616178, -1.4940211 ],
[-0.29338913, 1.59343441, 1.01659911, 0.05088553],
[-0.24299026, 0.82703222, -0.05034992, -0.7668296 ],
[-0.58743795, 1.72143897, 0.25810274, -0.26408754]])
df.to_numpy
를 사용하면, dataframe을 numpy array로 변환시켜 줄 수 있다.
이때, column 내에 다양한 dtype
이 존재하면 실행이 오래 걸릴 수 있다.
df.describe()
>>>
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean 0.076297 1.195471 -0.107369 -0.732621
std 0.895268 0.563517 1.004331 0.866661
min -0.797328 0.518531 -1.973643 -2.018436
25% -0.513926 0.755110 -0.148403 -1.312223
50% -0.268190 1.210233 0.103876 -0.515459
75% 0.675047 1.689438 0.279147 -0.027858
max 1.397870 1.781252 1.016599 0.096765
df.describe()
를 사용해 데이터의 기술 통계 값을 볼 수 있다.
이때, include = "object"
를 사용해 범주형 자료의 기술통계 값도 볼 수 있다.
df.T
>>>
2013-01-01 2013-01-02 2013-01-03 2013-01-04 2013-01-05 2013-01-06
A 1.397870 -0.797328 0.981060 -0.293389 -0.242990 -0.587438
B 1.781252 0.518531 0.731136 1.593434 0.827032 1.721439
C -0.181087 -1.973643 0.286162 1.016599 -0.050350 0.258103
D 0.096765 -2.018436 -1.494021 0.050886 -0.766830 -0.264088
.T
를 이용해 transpose 할 수 도 있다.
df.sort_index(axis=1, ascending = False)
>>>
D C B A
2013-01-01 0.096765 -0.181087 1.781252 1.397870
2013-01-02 -2.018436 -1.973643 0.518531 -0.797328
2013-01-03 -1.494021 0.286162 0.731136 0.981060
2013-01-04 0.050886 1.016599 1.593434 -0.293389
2013-01-05 -0.766830 -0.050350 0.827032 -0.242990
2013-01-06 -0.264088 0.258103 1.721439 -0.587438
.sort_index()
또는 .sort_values
를 통해 정렬할 수도 있다.
axis = 1
이므로 열 방향으로 정렬하는 모습이다.
기본값이 axis = 0, ascending = True
이다.
df["A"]
>>>
2013-01-01 1.397870
2013-01-02 -0.797328
2013-01-03 0.981060
2013-01-04 -0.293389
2013-01-05 -0.242990
2013-01-06 -0.587438
Freq: D, Name: A, dtype: float64
single column을 select 하기 위해서는 [](__getitem__)
을 사용한다.
. 도 쓸 수 있지만, 컬럼 명에 특수문자가 들어갈 경우 오류가 생길 수 있으니 권장하지 않는다.
df["20130102":"20130104"]
>>>
A B C D
2013-01-02 -0.797328 0.518531 -1.973643 -2.018436
2013-01-03 0.981060 0.731136 0.286162 -1.494021
2013-01-04 -0.293389 1.593434 1.016599 0.050886
한편, [] 를 이용해 slicing
을 할 때에는, 열이 아닌 행이 slicing 된다.
df.loc[dates[0]]
>>>
A 1.397870
B 1.781252
C -0.181087
D 0.096765
Name: 2013-01-01 00:00:00, dtype: float64
.loc
을 통해 selection by label
을 할 수 있다.
index
에 대해 선택한다고 생각할 수 있다.
이때 인자 값으로는 명시적이고 정확한 값이 사용된다.
df.loc[:,["A","B"]]
>>>
A B
2013-01-01 1.397870 1.781252
2013-01-02 -0.797328 0.518531
2013-01-03 0.981060 0.731136
2013-01-04 -0.293389 1.593434
2013-01-05 -0.242990 0.827032
2013-01-06 -0.587438 1.721439
행
을 전체에 대해 slicing
하고 열
에 대해 loc
을 사용할 수 도 있다.
이때, 여러 column이나 index 에 대해 사용할 때에는 해당 이름으로 된 list
를 만들어 사용한다.
df.loc["20130102":"20130104", ["A", "B"]]
>>>
A B
2013-01-02 -0.797328 0.518531
2013-01-03 0.981060 0.731136
2013-01-04 -0.293389 1.593434
loc의 slicing은 endpoints가 included
되어 실행된다.
df.loc["20130102",["A","B"]]
>>>
A -0.797328
B 0.518531
Name: 2013-01-02 00:00:00, dtype: float64
만약 선택한 값이 Series
로 표현되는 형태 일 경우, 자료형이 Series로 바뀌는 reduction in the dimensions 가 일어난다.
df.iloc[3]
>>>
A -0.293389
B 1.593434
C 1.016599
D 0.050886
Name: 2013-01-04 00:00:00, dtype: float64
명시적 방법이 아닌, selection by position 을 위해서는 iloc
을 사용한다.
df.iloc[3:5,0:2]
>>>
A B
2013-01-04 -0.293389 1.593434
2013-01-05 -0.242990 0.827032
iloc 에서의 slicing은 end point가 포함되지 않고
진행된다.
df.iloc[[1,2,4],[0,2]]
>>>
A C
2013-01-02 -0.797328 -1.973643
2013-01-03 0.981060 0.286162
2013-01-05 -0.242990 -0.050350
다음과 같이 fancy indexing을 사용할 수도 있다.
df[df["A"]>0]
>>>
A B C D
2013-01-01 1.39787 1.781252 -0.181087 0.096765
2013-01-03 0.98106 0.731136 0.286162 -1.494021
특정 조건에 만족하는 자료들만 select 하기 위해서는 boolean indexing
을 사용한다.
[]
안에 조건식을 넣어주면 된다.
df[df>0]
>>>
A B C D
2013-01-01 1.39787 1.781252 NaN 0.096765
2013-01-02 NaN 0.518531 NaN NaN
2013-01-03 0.98106 0.731136 0.286162 NaN
2013-01-04 NaN 1.593434 1.016599 0.050886
2013-01-05 NaN 0.827032 NaN NaN
2013-01-06 NaN 1.721439 0.258103 NaN
전체 df에서, 각 value의 값이 조건을 충족하는 것만 select하기 위해서는 다음과 같이 할 수 있다.
df2 = df.copy()
df2["E"] = ["one", "one", "two", "three", "four", "three"]
df2
>>>
A B C D E
2013-01-01 1.397870 1.781252 -0.181087 0.096765 one
2013-01-02 -0.797328 0.518531 -1.973643 -2.018436 one
2013-01-03 0.981060 0.731136 0.286162 -1.494021 two
2013-01-04 -0.293389 1.593434 1.016599 0.050886 three
2013-01-05 -0.242990 0.827032 -0.050350 -0.766830 four
2013-01-06 -0.587438 1.721439 0.258103 -0.264088 three
df2[df2["E"].isin(["two","four"])]
>>>
A B C D E
2013-01-03 0.98106 0.731136 0.286162 -1.494021 two
2013-01-05 -0.24299 0.827032 -0.050350 -0.766830 four
filtering을 위해서 .isin()
method를 사용한다.
.isin() method는 다음과 같이 사용할 수 있다. .isin([포함될 값들의 list])
-> 각 자료별로 T/F를 반환한다.
s1 = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range("20130102", periods=6))
df["F"] = s1
df
>>>
A B C D F
2013-01-01 1.397870 1.781252 -0.181087 0.096765 NaN
2013-01-02 -0.797328 0.518531 -1.973643 -2.018436 1.0
2013-01-03 0.981060 0.731136 0.286162 -1.494021 2.0
2013-01-04 -0.293389 1.593434 1.016599 0.050886 3.0
2013-01-05 -0.242990 0.827032 -0.050350 -0.766830 4.0
2013-01-06 -0.587438 1.721439 0.258103 -0.264088 5.0
새로운 column을 추가하기 위해서는 df["new"] = data
와 같이 정의해주기만 하면 된다.
df.loc[dates[0], "A"] = 0
df.iloc[0, 1] = 0
df
>>>
A B C D F
2013-01-01 0.000000 0.000000 -0.181087 0.096765 NaN
2013-01-02 -0.797328 0.518531 -1.973643 -2.018436 1.0
2013-01-03 0.981060 0.731136 0.286162 -1.494021 2.0
2013-01-04 -0.293389 1.593434 1.016599 0.050886 3.0
2013-01-05 -0.242990 0.827032 -0.050350 -0.766830 4.0
2013-01-06 -0.587438 1.721439 0.258103 -0.264088 5.0
특정 위치의 값을 바꾸어주기 위해서는 그 위치로 접근하여 바꾸어주면 된다.
df.loc[:, "D"] = np.array([5] * len(df))
df
>>>
A B C D F
2013-01-01 0.000000 0.000000 -0.181087 5 NaN
2013-01-02 -0.797328 0.518531 -1.973643 5 1.0
2013-01-03 0.981060 0.731136 0.286162 5 2.0
2013-01-04 -0.293389 1.593434 1.016599 5 3.0
2013-01-05 -0.242990 0.827032 -0.050350 5 4.0
2013-01-06 -0.587438 1.721439 0.258103 5 5.0
행이나 열 전체를 바꾸어 줄 수도 있다. len(df)
는 행의 개수를 의미한다.
df2 = df.copy()
df2[df2>0] = -df2
df2
>>>
A B C D F
2013-01-01 0.000000 0.000000 -0.181087 -5 NaN
2013-01-02 -0.797328 -0.518531 -1.973643 -5 -1.0
2013-01-03 -0.981060 -0.731136 -0.286162 -5 -2.0
2013-01-04 -0.293389 -1.593434 -1.016599 -5 -3.0
2013-01-05 -0.242990 -0.827032 -0.050350 -5 -4.0
2013-01-06 -0.587438 -1.721439 -0.258103 -5 -5.0
다음과 같이 사용할 수도 있다.
결측값을 표시하기 위해서는 주로 np.nan
을 사용한다.
df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ["E"])
df1.loc[dates[0]:dates[1],"E"] = 1
df1
A B C D F E
2013-01-01 0.000000 0.000000 -0.181087 5 NaN 1.0
2013-01-02 -0.797328 0.518531 -1.973643 5 1.0 1.0
2013-01-03 0.981060 0.731136 0.286162 5 2.0 NaN
2013-01-04 -0.293389 1.593434 1.016599 5 3.0 NaN
>>>
.reindex
은 index를 추가, 변경, 삭제 할 수 있는 메소드로, data의 copy를 제공한다.
결측값이 존재하는 df를 만들어주었다. (F의 0, E의 2,3)
df1.dropna(how = "any")
>>>
A B C D F E
2013-01-02 -0.797328 0.518531 -1.973643 5 1.0 1.0
결측값 삭제
df.dropna()
를 사용하면 결측값이 있는 모든 행을 삭제한다.(기본)
axis
인자로 열을 삭제하게 바꿀 수 도 있다.
how = "any"
또는 all
로 지정하여 한 값이라도 결측값일때, 혹은 모든 값이 결측값일때 삭제해주도록 변경한다.
df1.fillna(value=5)
>>>
A B C D F E
2013-01-01 0.000000 0.000000 -0.181087 5 5.0 1.0
2013-01-02 -0.797328 0.518531 -1.973643 5 1.0 1.0
2013-01-03 0.981060 0.731136 0.286162 5 2.0 5.0
2013-01-04 -0.293389 1.593434 1.016599 5 3.0 5.0
결측값 메꾸기
.fillna()
를 사용하면 결측값을 메꾸어 준다.
이때 값으로 평균이나 중간값을 사용할 수도 있겠다.
df.isna()
>>>
A B C D F
2013-01-01 False False False False True
2013-01-02 False False False False False
2013-01-03 False False False False False
2013-01-04 False False False False False
2013-01-05 False False False False False
2013-01-06 False False False False False
.isna()
는 모든 df에 대해 nan인지 여부를 boolean으로 표현해준다. 아까의 isin
과 비슷하다!
df.mean()
>>>
A -0.156681
B 0.898595
C -0.107369
D 5.000000
F 3.000000
dtype: float64
df.mean()
으로 column 별 평균을 계산한다.
df.mean(1)
>>>
2013-01-01 1.204728
2013-01-02 0.749512
2013-01-03 1.799672
2013-01-04 2.063329
2013-01-05 1.906738
2013-01-06 2.278421
Freq: D, dtype: float64
다른 axis에 대해 수행해줄 수도 있다.
s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(2)
s
>>>
2013-01-01 NaN
2013-01-02 NaN
2013-01-03 1.0
2013-01-04 3.0
2013-01-05 5.0
2013-01-06 NaN
Freq: D, dtype: float64
df.sub(s, axis="index")
>>>
A B C D F
2013-01-01 NaN NaN NaN NaN NaN
2013-01-02 NaN NaN NaN NaN NaN
2013-01-03 -0.018940 -0.268864 -0.713838 4.0 1.0
2013-01-04 -3.293389 -1.406566 -1.983401 2.0 0.0
2013-01-05 -5.242990 -4.172968 -5.050350 0.0 -1.0
2013-01-06 NaN NaN NaN NaN NaN
만약 다른 차원의 값이거나 정렬이 필요하면, 판다스가 자동으로 broadcasting
하여 진행한다.
index 별로 series s를 빼주는 연산. 이때, nan이 모두 nan이 된다.
df.apply(np.cumsum)
>>>
A B C D F
2013-01-01 0.000000 0.000000 -0.181087 5 NaN
2013-01-02 -0.797328 0.518531 -2.154730 10 1.0
2013-01-03 0.183732 1.249667 -1.868569 15 3.0
2013-01-04 -0.109657 2.843102 -0.851969 20 6.0
2013-01-05 -0.352648 3.670134 -0.902319 25 10.0
2013-01-06 -0.940086 5.391573 -0.644217 30 15.0
df.apply()
를 사용하여 data에 user defined function을 적용시킬 수 있다.
함수를 정의하여서도, lambda
함수와도 많이 사용한다.
np.cumsum
은 축적된 합(cumulative sum)을 구한다.
각 column
에 대해 적용된다.
df.apply(lambda x : x.max() - x.min())
>>>
A 1.778388
B 1.721439
C 2.990242
D 0.000000
F 4.000000
dtype: float64
lambda 함수와 함께 사용한다.
s = pd.Series(np.random.randint(0, 7, size=10))
s.value_counts()
>>>
3 2
1 2
2 2
0 2
5 1
6 1
dtype: int64
.value_counts()
를 사용해 도수를 구할 수 있다.
normalize = True
를 인자로 주어 상대도수를 알 수도 있다.(비율)
s = pd.Series(["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"])
s.str.lower()
>>>
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
.str
접근자를 사용하여 다양한 string method를 사용한다.
pattern matching 에 대해 정규표현식을 사용하기도 한다.
오늘은 이렇게 python의 operation 기능까지 10 minutes to pandas를 통해 알아보았다.
기본을 파악했으니, 자세히 공부하고 실습을 진행하며 다져나가자!
[참고] 10 minutes to pandas