본문 바로가기
ML&DATA/python for data analysis

pandas - (재색인, 중복 색인 등 핵심 기능)

by sun__ 2020. 7. 20.

5.2 핵심 기능

Series와 DataFrame을 다루는 기본적인 방법 설명

 

5.2.1 재색인

reindex: 새로운 색인에 맞도록 정렬된 객체를 새로 생성한다. 존재하지 않은 색인값이 있으면 NaN추가한다.

obj = pd.Series([4.5, 7.2, -5.3, 3.6], index = ['d', 'b', 'a', 'c'])
obj
#out:
#d    4.5
#b    7.2
#a   -5.3
#c    3.6
#dtype: float64

obj2 = obj.reindex(['a','b','c','d','e']) #obj 자체는 바뀌지 않는다.
obj2
#out:
#a   -5.3
#b    7.2
#c    3.6
#d    4.5
#e    NaN
#dtype: float64

 

reindex의 method 옵션에 'ffill'을 넣어주면 누락된 값을 직전의 값으로 채워준다. 

('bfill'은 다음 값을 채워준다.)

obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj3
#out:
#0      blue
#2    purple
#4    yellow
#dtype: object

obj3.reindex(range(6), method='ffill')
#out:
#0      blue
#1      blue
#2    purple
#3    purple
#4    yellow
#5    yellow
#dtype: object

 

DataFrame의 reindex의 경우 로우, 컬럼 또는 둘 다 변경할 수 있다. 컬럼을 재색인 할 땐 column예약어 사용.

frame = pd.DataFrame(np.arange(9).reshape((3,3)),
                    index = ['a','c','d'],
                    columns = ['Ohio', 'Texas', 'California'])
frame

frame.reindex(['a','b','c','d'])

frame.reindex(columns = ['Texas', 'Utah', 'California'])

 

 

reindex보단 loc을 사용해서 라벨로 색인하면 좀 더 간결하다. 더 많이쓰이는 방법

frame.loc[['a','b','c','d'], ['Texas', 'Utah', 'California']]

 

 

5.2.2 하나의 로우나 컬럼 삭제하기

 

drop 메서드로 선택한 값들이 삭제된 새로운 객체를 얻을 수 있다. 

inplace 옵션을 True로 주면 실제로 값을 삭제할 수 있다.

 

Series의 경우 다음과 같이 사용

obj = pd.Series(np.arange(5.), index=['a','b','c','d','e'])
obj
#out:
#a    0.0
#b    1.0
#c    2.0
#d    3.0
#e    4.0
#dtype: float64

obj.drop('c')
#out:
#a    0.0
#b    1.0
#d    3.0
#e    4.0
#dtype: float64

obj.drop(['a','b'])
#out:
#c    2.0
#d    3.0
#e    4.0
#dtype: float64

obj.drop('c', inplace=True)
obj
#out:
#a    0.0
#b    1.0
#d    3.0
#e    4.0
#dtype: float64

 

DataFrame의 경우 다음과 같이 사용

data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
data

data.drop(['Colorado', 'Ohio'])

data.drop(['two','four'], axis=1)

 

5.2.3 색인하기, 선택하기, 거르기

Series의 색인은 사전과 배열의 혼합의 형태로 동작한다. 라벨 이름으로 슬라이싱하면 끝점도 포함한다.

obj = pd.Series(np.arange(4.), index=['a','b','c','d'])
obj
#out:
#a    0.0
#b    1.0
#c    2.0
#d    3.0
#dtype: float64

obj['b']	#out : 1.0
obj[1]		#out : 1.0

obj['b':'c']	
#out : 
#b    1.0
#c    2.0
#dtype: float64

obj[1:2]
#out:
#b    1.0
#dtype: float64

obj[['b','a','d']]
#b    1.0
#a    0.0
#d    3.0
#dtype: float64

obj[[1,3]]
#b    1.0
#d    3.0
#dtype: float64

obj[obj<2.0]
#a    0.0
#b    1.0
#dtype: float64

 

슬라이싱 문법으로 선택된 영역(view)에 스칼라 값을 대입하는 것도 numpy에서와 유사하게 동작한다.

obj['b':'c'] = 5
obj
#a    0.0
#b    5.0
#c    5.0
#d    3.0
#dtype: float64

 

 

DataFrame에선 인덱스로 하나 이상의 컬럼 값을 가져올 수 있다.

data = pd.DataFrame(np.arange(16).reshape((4, 4)),
                    index=['Ohio', 'Colorado', 'Utah', 'New York'],
                    columns=['one', 'two', 'three', 'four'])
data

data['two']
#Ohio         1
#Colorado     5
#Utah         9
#New York    13
#Name: two, dtype: int32

data[['three', 'one']]

 

반면에 슬라이싱이나 불리언배열을 이용하면 로우가 선택된다. 

data[:2]

data[data['three']>5]

data[data<5]=0
data

 

 

loc, iloc : DataFrame의 로우에 대한 라벨로 인덱싱 하는 방법

축 이름을 선택할 때는 loc을, 정수 색인으로 선택할 때는 iloc을 사용. 슬라이스도 지원한다.

data.loc['Colorado']	#data.iloc[1] 동치
#one      0
#two      5
#three    6
#four     7
#Name: Colorado, dtype: int32
#Series객체 반환. 객체 자체의 name이 Colorado가 됨.

data.loc['Colorado', ['two','three']]	#data.iloc[1, [1,2]] 동치
#two      5
#three    6
#Name: Colorado, dtype: int32
data.loc[:'Utah', 'two']
#Ohio        0
#Colorado    5
#Utah        9
#Name: two, dtype: int32

data.loc[:'Utah', :'two']

data.iloc[:, :3][data.three>=0]

data.iloc[:, :3][data.three>5]

 

 

 

5.2.4 정수 색인

Series에서 라벨 색인은 곧 Index객체에 담긴 값을 의미하고, 정수색인은 Series를 배열로 봤을 때 순서대로 매겨지는 인덱스이다.

ser = pd.Series(np.arange(3.))
ser[:1]

위 코드에서 슬라이싱이 라벨 색인 기준으로 이뤄진다면 끝점을 포함할 것이고 정수색인을 기준으로 이뤄지면 끝점이 포함되지 않을 것이다. 이런 모호함때문에 라벨에 대해선 loc을 정수 색인에 대해선 iloc사용이 권장된다.

(위 예제는 끝점을 포함하지 않는다.)

ser.loc[:1]
#0    0.0
#1    1.0
#dtype: float64

ser.iloc[:1]
#0    0.0
#dtype: float64

 

 

5.2.5 산술연산과 데이터 정렬

 

Series간 산술연산은 https://suuntree.tistory.com/263의 'join'검색해서 확인.

외부 조인과 유사하게 동작한다고 생각할 수 있음.

 

DataFrame의 경우 로우와 컬럼 모두에 적용된다. 공통되는 컬럼 라벨이나 로우 라벨이 없는 DataFrame을 더하면 결과에 NaN이 나타난다.

df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
                   index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
                   index=['Utah', 'Ohio', 'Texas', 'Oregon'])

df1
df2

 

df1+df2

df1에만 존재하는 c, Colardo와 df2에만 존재하는 e, Utah, Oregon에 해당하는 Series는 모두 NaN이 되는 것을 볼 수 있다.

 

만약 df1.add(df2, fill_value=0) 이런식으로 사용하면, df1에만 있고 df2엔 없는 요소는 0으로 간주한다.

df1.add(df2, fill_value=0)

 

5.2.5.1 DataFrame과 Series 간 연산

다음 예제를 살펴보자

arr = np.arange(12.).reshape((3, 4))
arr
#array([[ 0.,  1.,  2.,  3.],
#       [ 4.,  5.,  6.,  7.],
#       [ 8.,  9., 10., 11.]])

arr[0]
#array([0., 1., 2., 3.])

arr - arr[0] #broadcasting
#array([[0., 0., 0., 0.],
#       [4., 4., 4., 4.],
#       [8., 8., 8., 8.]])

2차원 배열 arr에서 arr[0]을 뺐더니 계산이 각 로우에 대해 한 번씩 수행된다. 이를 브로드캐스팅이라 한다.

 

 

DataFrame과 Series간의 연산은 이와 유사하다.

frame = pd.DataFrame(np.arange(12.).reshape((4,3)),
                     columns = list('bde'),
                     index = ['Utah', 'Ohio', 'Texas', 'Oregon'])

ser = frame.iloc[0]
#b    0.0
#d    1.0
#e    2.0
#Name: Utah, dtype: float64

frame
frame-ser

 

위와 같이 DataFrame과 Series 간의 연산은 Series의 색인을 DataFrame의 컬럼에 맞추고 아래 로우로 전파한다.

 

만약 색인값을 DataFrame의 컬럼이나 Series의 색인에서 찾을 수 없다면 그 객체는 형식을 맞추기 위해 재색인된다.

ser2 = pd.Series(range(3), index=list('bef'))
#b    0
#e    1
#f    2
#dtype: int64

frame
frame+ser2

 

 

만약 각 로우에 대한 연산을 수행하고자 한다면 메서드를 사용하면된다.

ser3 = frame['d']
#Utah       1.0
#Ohio       4.0
#Texas      7.0
#Oregon    10.0
#Name: d, dtype: float64

frame

 

frame.sub(ser3, axis='index')

axis = 0을 사용해도 된다.

 

 

5.2.6 함수 적용과 매핑

 

pandas 객체에도 numpy의 유니버셜 함수(배열의 각 원소에 적용되는 메서드 abs, sum등)를 적용할 수 있다.

frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
                     index=['Utah', 'Ohio', 'Texas', 'Oregon'])

 

frame
np.abs(frame)

 

각 컬럼이나 로우의 1차원 배열에 함수를 적용할 수도 있다. DataFrame의 apply를 이용해서 수행 가능. 이 때 apply는 실제로 값을 변경시키지 않고 변경시킨 것의 copy를 반환한다.

f = lambda x: x.max() - x.min()
frame.apply(f)
#b    0.877927
#d    4.139695
#e    0.826729
#dtype: float64

frame.apply(f, axis='columns')
#Utah      1.359624
#Ohio      1.042000
#Texas     2.149365
#Oregon    1.302609
#dtype: float64

 

apply 메서드에 전달된 함수가 Series를 반환할 경우 결과는 DataFrame이다

def f(x):
    return pd.Series([x.min(), x.max()], index=['min', 'max'])

frame.apply(f)
frame.apply(f, axis = 'columns')

 

파이썬에선 c의 printf와 유사하게 문자열 포맷을 정할 수 있다.

ex) printf("c스타일 : %.2f", 2.34) // 'python스타일 : %.2f" % 2.34

배열의 각 원소에 적용되는 함수를 사용할 수도 있다. applymap메서드를 사용.

ex) frame객체에서 실수값을 문자열 포맷으로 변환하는 예

format = lambda x : '%.2f' % x
frame.applymap(format)

이 메서드의 이름이 applymap인 이유는 Series는 각 원소에 적용할 함수를 지정하기 위해 map메서드를 사용하기 때문.

 

 

5.2.7 정렬과 순위

 

Series, DataFrame에서 로우나 컬럼의 색인이 오름차순으로 정렬된 객체를 반환하려면 sort_index()메서드를 사용하면 된다.

obj = pd.Series(range(4), index=['d','a','b','c'])
obj.sort_index()
#a    1
#b    2
#c    3
#d    0
#dtype: int64

frame = pd.DataFrame(np.arange(8).reshape((2,4)),
                     index = ['three', 'one'],
                     columns = ['d','a','b','c'])

 

frame
frame.sort_index()
frame.sort_index(axis=1)

 

ascending=False옵션으로 내림차순 정렬할 수 있다.

 

Series 객체를 값에 따라 정렬하고 싶으면 sort_values 메서드 사용하면 된다.

 

DataFrame 객체를 값에 따라 정렬할 때도 sort_values메서드를 사용하되 어떤 컬럼을 기준으로 할건지 by옵션을 반드시 정해줘야 한다. by에 여러 컬럼을 리스트로 줄 수도 있다.

frame = pd.DataFrame({'b':[4,7,-3,2], 'a':[0,1,0,1]})

frame
frame.sort_values(by='b')

 

정렬할 때 NaN값은 Series객체에서 가장 마지막에 위치하게 된다.

 

 

순위는 정렬과 거의 흡사하다. 1부터 배열의 유효한 데이터 개수까지 순서를 매긴다. 기본적으로 동점인 항목에 대해선 평균 순위를 매긴다. 

ascending옵션에 False를 주면 내림차순으로 순위를 매긴다.

method 옵션에 'min'를 주면 동점자들을 가장 높은(값은 낮음) 순위를 매긴다.

method 옵션에 'max'를 주면 동점자들을 가장 낮은(값은 높음) 순위를 매긴다.

method 옵션에 'first'를 주면 동점자 처리를 데이터 상에서 나나타는 순서에 따라 매긴다.

 

Series의 경우 

obj = pd.Series([7,-5,7,4,2,0,4])
obj.rank()
#0    6.5
#1    1.0
#2    6.5
#3    4.5
#4    3.0
#5    2.0
#6    4.5
#dtype: float64

obj.rank(method='first')
#0    6.0
#1    1.0
#2    7.0
#3    4.0
#4    3.0
#5    2.0
#6    5.0
#dtype: float64

 

DataFrame에서는 로우나 컬럼에 대해 순위를 정할 수 있다.

frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
                      'c': [-2, 5, 8, -2.5]})

frame
frame.rank(axis='columns')

 

 

5.2.8 중복 색인

 

중복된 색인값을 갖는 Series객체를 살펴보자. index속성의 is_unique속성은 색인값들이 유일한지 알려준다.

obj = pd.Series(range(5), index=['a','a','b','b','c'])
obj
#a    0
#a    1
#b    2
#b    3
#c    4
#dtype: int64

obj.index.is_unique	#out: False

 

중복되는 색인값이 없다면 색인을 이용해서 데이터에 접근했을 때 스칼라값이 나오지만 중복되는 색인 값이 있다면 Series객체를 반환한다.

obj['a']
#a    0
#a    1
#dtype: int64

obj['c']	#out: 4

 

DataFrame에서 로우를 선택하는 것도 동일하다.

df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])

df
df.loc['b']