pandas.DataFrame 임의의 위치 데이터를 획득하거나 변경(대입)하는 경우에 pandas.DataFrame의 속성인 loc, iloc, at, iat을 사용할 수 있다.
먼저 네 가지는 다음과 같은 차이점이 있다.
loc | iloc | at | iat | |
절대 좌표(위치) 지정 | x | o | x | o |
라벨명 지정 | o | x | o | x |
여러개의 요소 지정 | o | o | x | x |
슬라이스 표기 | o | o | x | x |
1) 좌표(위치)의 지정 방법
- at, loc : 행 명(행 라벨), 열 명(열 라벨)
- iat, iloc : 행 번호, 열 번호(절대 좌표(위치)지정)
2) 선택하여 확인, 변경할 수 있는 데이터
- at, iat : 하나의 요소 값
- loc, iloc : 하나 혹은 여러 개의 요소 값
- 리스트, 슬라이스로 범위를 지정할 수 있다.
- 행, 열을 선택하여 값을 획득하고 변경할 수 있다.
3) 그 외의 차이점
- 처리 속도는 at 과 iat의 경우가 loc과 iloc보다 빠르다
- 라벨과 번호를 합쳐서 위치를 지정하고 싶은 경우는 at 혹은 loc과 index나 column을 조합한다.
이번 포스팅에서는 다음의 내용에 대해 다룰 것이다.
- at, iat : 하나의 요소 값을 선택, 획득, 변경
- loc, iloc : 하나 혹은 여러 개의 요소 값을 선택, 획득, 변경
- 하나의 요소 값을 선택
- 여러 개의 요소 값을 선택
- 행, 열을 선택
- 행 명, 열 명이 중복된 값을 가진 경우
- 번호와 라벨 단위를 지정
- 행을 pandas.Series로 선택할 때 묵시적형 변환
참고로 pandas.DataFrame의 행, 열, pandas.Series의 요소 값을 선택, 획득하는 경우는 인덱스 참고 df[]도 사용할 수 있다. 또한 DataFrame.get_value(), DataFrame.ix[]도 있지만, 둘 다 최신 버전에서는 추천하지 않는다.
이번 샘플 코드에서는 아래의 csv 데이터를 read_csv로 읽어들여 사용한다. 인수 index_col로 맨 처음의 행을 index로 사용하고 있다.
import pandas as pd
df = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0)
print(df)
# age state point
# name
# Alice 24 NY 64
# Bob 42 CA 92
# Charlie 18 CA 70
# Dave 68 TX 70
# Ellen 24 CA 88
# Frank 30 NY 57
print(df.index.values)
# ['Alice' 'Bob' 'Charlie' 'Dave' 'Ellen' 'Frank']
print(df.columns.values)
# ['age' 'state' 'point']
at, iat : 하나의 요소 값을 선택, 획득, 변경
at은 행 명과 열 명으로 위치를 지정한다. 데이터를 획득하기 위할 뿐만 아니라, 그 위치에 새로운 값을 설정(대입)하는 것도 가능하다.
print(df.at['Bob', 'age'])
print(df.at['Dave', 'state'])
# 42
# TX
df.at['Bob', 'age'] = 60
print(df.at['Bob', 'age'])
# 60
iat은 행 번호와 열 번호로 위치를 지정한다. 행 번호, 열 번호는 0부터 시작한다.
iat도 at과 동일하게, 데이터를 획득할 뿐 아니라 그 위치에 새로운 값을 설정(대입)할 수 있다.
print(df.iat[1, 0])
print(df.iat[3, 1])
# 60
# TX
df.iat[1, 0] = 42
print(df.iat[1, 0])
# 42
loc, iloc : 하나 혹은 여러 개의 요소 값을 선택, 획득, 변경
loc과 iloc은 하나의 값뿐 아니라, 범위를 지정하여 여러 개의 데이터를 선택할 수 있다. loc은 행 명과 열 명으로 위치를 지정하고, iloc은 행 번호와 열 번호로 위치를 지정한다.
하나의 요소 값을 선택
하나의 값에 액세스할 경우 at, iat와 동일하다. 처리 속도는 앞에서 말했듯 at, iat의 쪽이 빠르다.
print(df.loc['Bob', 'age'])
print(df.iloc[3, 1])
# 42
# TX
데이터를 참고할 뿐만 아니라, 그 위치에 새로운 값을 설정(대입)하는 것도 가능하다.
df.loc['Bob', 'age'] = 60
print(df.loc['Bob', 'age'])
# 60
df.iloc[1, 0] = 42
print(df.iloc[1, 0])
# 42
여러 개의 요소 값을 선택
여러 개의 값에 액세스할 경우는 리스트[a, b, c, ...]나 슬라이스 start:stop:step으로 데이터의 범위, 위치를 지정할 수 있다. pandas.Series 혹은 pandas.DataFrame가 반환된다.
슬라이스는 보통의 슬라이스 작성법과 같다. step은 생략가능하다.
슬라이스 start:stop:step으로 지정할 때, iloc으로 행 번호, 열 번호를 사용하는 경우 일반적인 슬라이스와 동일하게 stop의 한 단계 전까지이지만, loc으로 행 명, 열 명을 사용하는 경우는 stop의 값도 포함되므로 주의가 필요하다.
print(df.loc['Bob':'Dave', 'age'])
print(type(df.loc['Bob':'Dave', 'age']))
# name
# Bob 42
# Charlie 18
# Dave 68
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
print(df.loc[:'Dave', ['age', 'point']])
print(type(df.loc[:'Dave', 'age':'point']))
# age point
# name
# Alice 24 64
# Bob 42 92
# Charlie 18 70
# Dave 68 70
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:3, [0, 2]])
print(type(df.iloc[:3, [0, 2]]))
# age point
# name
# Alice 24 64
# Bob 42 92
# Charlie 18 70
# <class 'pandas.core.frame.DataFrame'>
step을 지정하면, 홀수행 혹은 짝수행을 추출하여 획득하는 것도 가능하다.
print(df.iloc[::2, 0])
print(type(df.iloc[::2, 0]))
# name
# Alice 24
# Charlie 18
# Ellen 24
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
print(df.iloc[1::2, 0])
print(type(df.iloc[1::2, 0]))
# name
# Bob 42
# Dave 68
# Frank 30
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
여러 개의 값을 일괄로 변경하는 것도 가능하다.
df.loc['Bob':'Dave', 'age'] = [20, 30, 40]
print(df.loc['Bob':'Dave', 'age'])
# name
# Bob 20
# Charlie 30
# Dave 40
# Name: age, dtype: int64
행, 열을 선택
인덱스 참조 df[]로 행, 열을 선택할 수 있지만, 아래의 지정 방법에 한정된다.
- 행의 선택 : 행 이름, 행 번호의 인덱스
- 열의 선택 : 열 이름, 혹은 열 이름의 리스트
print(df['Bob':'Ellen'])
# age state point
# name
# Bob 20 CA 92
# Charlie 30 CA 70
# Dave 40 TX 70
# Ellen 24 CA 88
print(df[:3])
# age state point
# name
# Alice 24 NY 64
# Bob 20 CA 92
# Charlie 30 CA 70
print(df['age'])
# name
# Alice 24
# Bob 20
# Charlie 30
# Dave 40
# Ellen 24
# Frank 30
# Name: age, dtype: int64
print(df[['age', 'point']])
# age point
# name
# Alice 24 64
# Bob 20 92
# Charlie 30 70
# Dave 40 70
# Ellen 24 88
# Frank 30 57
loc, iloc으로 행, 열을 선택할 경우는 인덱스 참고 df[]보다 유연하게 지정할 수 있다.
loc, iloc에서 열의 지정을 생략하면 열 참조가 된다. 인덱스 참조에서는 할 수 없는 행 이름, 열 번호 단독 지정이나 리스트에 의한 지정도 가능하다.
print(df.loc['Bob'])
print(type(df.loc['Bob']))
# age 20
# state CA
# point 92
# Name: Bob, dtype: object
# <class 'pandas.core.series.Series'>
print(df.iloc[[1, 4]])
print(type(df.iloc[[1, 4]]))
# age state point
# name
# Bob 20 CA 92
# Ellen 24 CA 88
# <class 'pandas.core.frame.DataFrame'>
loc, iloc으로 행의 지정을 : (전체의 슬라이스)로 하면 열을 참조할 수 있다. 인덱스 참조에서는 할 수 없는 슬라이스에 의한 지정도 가능하다. iloc으로 열 번호를 사용하는 것도 가능하다.
print(df.loc[:, 'age':'point'])
print(type(df.loc[:, 'age':'point']))
# age state point
# name
# Alice 24 NY 64
# Bob 20 CA 92
# Charlie 30 CA 70
# Dave 40 TX 70
# Ellen 24 CA 88
# Frank 30 NY 57
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:, [0, 2]])
print(type(df.iloc[:, [0, 2]]))
# age point
# name
# Alice 24 64
# Bob 20 92
# Charlie 30 70
# Dave 40 70
# Ellen 24 88
# Frank 30 57
# <class 'pandas.core.frame.DataFrame'>
행 이름-행 번호, 열 이름-열 번호를 하나로 지정하여 하나의 행-하나의 열을 선택하는 경우는 pandas.Series가 반환되지만, 동일한 하나의 행 - 하나의 열을 선택하는 경우에도 슬라이스나 리스트로 지정한 경우는 pandas.DataFrame이 된다.
print(df.loc['Bob'])
print(type(df.loc['Bob']))
# age 20
# state CA
# point 92
# Name: Bob, dtype: object
# <class 'pandas.core.series.Series'>
print(df.loc['Bob':'Bob'])
print(type(df.loc['Bob':'Bob']))
# age state point
# name
# Bob 20 CA 92
# <class 'pandas.core.frame.DataFrame'>
print(df.loc[['Bob']])
print(type(df.loc[['Bob']]))
# age state point
# name
# Bob 20 CA 92
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:, 1])
print(type(df.iloc[:, 1]))
# name
# Alice NY
# Bob CA
# Charlie CA
# Dave TX
# Ellen CA
# Frank NY
# Name: state, dtype: object
# <class 'pandas.core.series.Series'>
print(df.iloc[:, 1:2])
print(type(df.iloc[:, 1:2]))
# state
# name
# Alice NY
# Bob CA
# Charlie CA
# Dave TX
# Ellen CA
# Frank NY
# <class 'pandas.core.frame.DataFrame'>
print(df.iloc[:, [1]])
print(type(df.iloc[:, [1]]))
# state
# name
# Alice NY
# Bob CA
# Charlie CA
# Dave TX
# Ellen CA
# Frank NY
# <class 'pandas.core.frame.DataFrame'>
특히, 행을 pandas.Series로 선택하면 암묵적 형변환이 이뤄질 가능성이 있으므로 주의할 필요가 있다.
행 명과 열 명이 중복된 값을 가지고 있는 경우
행 명(행 라벨) index, 열 명(열 라벨) columns에 중복된 값이 포함되어 있어도 에러가 발생하지 않는다.
중복된 값을 가진 열을 index에 지정한 경우 다음과 같은 예가 된다.
df_state = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=2)
print(df_state)
# name age point
# state
# NY Alice 24 64
# CA Bob 42 92
# CA Charlie 18 70
# TX Dave 68 70
# CA Ellen 24 88
# NY Frank 30 57
print(df_state.index.values)
# ['NY' 'CA' 'CA' 'TX' 'CA' 'NY']
at으로 중복된 열 이름을 지정하면, numpy.ndarray로 여러 개의 값이 반환되어 온다.
print(df_state.at['NY', 'age'])
print(type(df_state.at['NY', 'age']))
# [24 30]
# <class 'numpy.ndarray'>
loc으로 중복된 행 명을 지정하면, pandas.DataFrame 혹은 pandas.Series가 반환된다.
print(df_state.loc['NY', 'age'])
print(type(df_state.loc['NY', 'age']))
# state
# NY 24
# NY 30
# Name: age, dtype: int64
# <class 'pandas.core.series.Series'>
print(df_state.loc['NY', ['age', 'point']])
print(type(df_state.loc['NY', ['age', 'point']]))
# age point
# state
# NY 24 64
# NY 30 57
# <class 'pandas.core.frame.DataFrame'>
iat이나 iloc에서 행 번호으로 지정하는 경우는 행 번호가 중복되어 있어도 특히 문제가 없다.
print(df_state.iat[0, 1])
# 24
혼란의 근원이되므로, 강력한 이유가 없다면 지정할 때, 행 이름-열 이름은 임의의 값으로 하는 편이 좋다.
행 이름 - 열 이름이 임의의 값으로 되어 있는지 아닌지(중복되는지 아닌지)는 index.is_unique, columns.is_unique로 확인할 수 있다.
print(df_state.index.is_unique)
# False
print(df_state.columns.is_unique)
# True
번호와 라벨로 위치를 지정하기
행 번호와 열 라벨과 같이 번호와 라벨의 조합으로 위치를 지정하고 싶은 경우, at 혹은 loc과 index나 columns를 사용하는 방법이 있다.
index나 columns로 행 번호 혹은 열 변호로부터 그 행 라벨, 열 라벨을 획득할 수 있다.
print(df)
# age state point
# name
# Alice 24 NY 64
# Bob 20 CA 92
# Charlie 30 CA 70
# Dave 40 TX 70
# Ellen 24 CA 88
# Frank 30 NY 57
print(df.index[2])
# Charlie
print(df.columns[1])
# state
이것과 at 혹은 loc를 사용하여, 번호와 라벨의 조합으로 위치를 지정할 수 있다.
print(df.at[df.index[2], 'age'])
# 30
print(df.loc[['Alice', 'Dave'], df.columns[1]])
# name
# Alice NY
# Dave TX
# Name: state, dtype: object
위에서 서술했듯이, 슬라이스로 지정할 때, loc으로 행 라벨, 열 라벨을 사용하는 경우는 stop까지 포함되지만, iloc에서 행 번호, 열 번호를 사용하는 경우는 stop의 하나 앞까지가 된다. stop의 값을 번호로부터 라벨으로 변환하는 경우 index[n-1]로 할 필요가 있으므로 주의하자.
아래와 같이 []나 loc, iloc을 반복해 작성할 수 있지만, 이것은 chained indexing이라는 것으로 SettingWithCopyWarning이라는 경고의 원인이 된다.
또한, 다음에 설명하는 것과 같이, 하나의 행을 선택할 경우는 암묵적 형변환이 일어나는 경우가 있다. 앞에서 표시된 index나 column을 사용하여 하나의 at이나 loc으로 묶어서 사용하는 편이 좋다.
print(df['age'][2])
# 30
print(df.age[2])
# 30
print(df.loc[['Alice', 'Dave']].iloc[:, 1])
# name
# Alice NY
# Dave TX
# Name: state, dtype: object
행을 pandas.Series로 선택할 때의 묵시적 형변환
loc이나 iloc으로 하나의 행을 pandas.Series로 선택해 획득할 경우, 데이터형 dtype으로 통일되므로, 원래의 pandas.DataFrame 행 데이터 형이 달라지는 묵시적 형 변환이 일어난다.
정수 int의 열와 부동 소수점 수 float을 가진 pandas.DataFrame을 통해 살펴보자.
df_mix = pd.DataFrame({'col_int': [0, 1, 2], 'col_float': [0.1, 0.2, 0.3]}, index=['A', 'B', 'C'])
print(df_mix)
# col_int col_float
# A 0 0.1
# B 1 0.2
# C 2 0.3
print(df_mix.dtypes)
# col_int int64
# col_float float64
# dtype: object
loc이나 iloc으로 1행을 획득하면, float의 pandas.Series가 된다. int의 열에 있던 데이터는 float로 변환된다는 것이다.
print(df_mix.loc['B'])
# col_int 1.0
# col_float 0.2
# Name: B, dtype: float64
print(type(df_mix.loc['B']))
# <class 'pandas.core.series.Series'>
다음과 같이 []을 연속해서 작성하면, float로 변환된 pandas.Series의 요소가 획득되게 된다. 원래 데이터형과 다른 데이터형으로 변환된 데이터 값을 얻게 되므로 주의해야한다.
print(df_mix.loc['B']['col_int'])
# 1.0
print(type(df_mix.loc['B']['col_int']))
# <class 'numpy.float64'>
따라서 이러한 현상을 피하려면, at이나 iat을 사용하는 편이 좋다. at이나 iat이면 원래 데이터형 그대로 값을 취득할 수 있다.
print(df_mix.at['B', 'col_int'])
# 1
print(type(df_mix.at['B', 'col_int']))
# <class 'numpy.int64'>
loc이나 iloc으로 1개의 리스트로 지정하는 경우, pandas.Series가 아닌 하나의 pandas.DataFrame행이 된다. 당연하지만, 이 경우는 원래 데이터형 dtype이 유지된다.
print(df_mix.loc[['B']])
# col_int col_float
# B 1 0.2
print(type(df_mix.loc[['B']]))
# <class 'pandas.core.frame.DataFrame'>
print(df_mix.loc[['B']].dtypes)
# col_int int64
# col_float float64
# dtype: object
참고자료
'IT > 언어' 카테고리의 다른 글
[python] matplotlib의 figure(그림)과 axes(좌표축) (0) | 2021.09.07 |
---|---|
[python] assert문 간단 사용법 (0) | 2021.09.01 |
[python] matplotlib 사용법 기초 (0) | 2021.08.09 |
[python] ArgumentParser 사용법 (3) | 2021.08.02 |
[python] python으로 excel을 조작할 수 있는 openpyxl 사용법 (0) | 2021.07.27 |