달나라 노트

Python Pandas : DataFrame.loc & DataFrame.iloc 본문

Python/Python Pandas

Python Pandas : DataFrame.loc & DataFrame.iloc

CosmosProject 2020. 11. 4. 20:09
728x90
반응형

 

 

 

DataFrame.loc & DataFrame.iloc

DataFrame의 loc, iloc는 DataFrame에서 내가 원하는 행 또는 내가 원하는 컬럼만을 추출할 수 있게 해줍니다.

 

먼저 test용 DataFrame을 생성합니다.

import pandas as pd
dict_name = {
    'item_id': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'item_name': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'status': [1, 1, 1, 0, 0, 1, 0, 1, 1, 1]
}
df_name = pd.DataFrame(dict_name)

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
0        1         a       1
1        2         b       1
2        3         c       1
3        4         d       0
4        5         e       0
5        6         f       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
<class 'pandas.core.frame.DataFrame'>
  




 

 

 


 

 

 

 

 

 

loc부터 알아보겠습니다.

먼저 loc는 대괄호를 콤마로 구분하고 콤마의 왼쪽은 '추출할 row name'을 적으며

오른쪽에는 '추출할 column name'을 적습니다.

DataFrame.loc[추출할 row name, 추출할 column name]

 

 

 

 

아래처럼 추출할 row name과 추출할 column name을 대괄호로 묶어서 제시하면,

Output에서 볼 수 있듯이 row name이 2, 3인 item_name 컬럼의 데이터만 따로 떨어져나온듯한 DataFrame이 반환되었습니다.

df_name = df_name.loc[[2, 3], ['item_name']]

print(df_name)
print(type(df_name))
 
- Output
  item_name
2         c
3         d
<class 'pandas.core.frame.DataFrame'>
  

 

 

 

한 가지 주의할 점은 추출할 컬럼은 item_name 한 개였기 때문에 아래 예시처럼 컬럼이름은 대괄호로 묶지 않아도 loc는 정상적으로 작동이 되지만 반환되는 결과가 Series입니다.

DataFrame을 반환받고 싶다면 반드시 row name과 column name을 대괄호로 묶어서 사용합니다.

df_name = df_name.loc[[2, 3], 'item_name']

print(df_name)
print(type(df_name))
 
- Output
2    c
3    d
<class 'pandas.core.series.Series'>
  




 

 


 

 

 

 

 

import pandas as pd

dict_test = {
    'col1': [1, 2, 3, 4, 5],
    'col2': ['a', 'b', 'c', 'd', 'e'],
    'col3': [6, 7, 8, 9, 10]
}

df_test = pd.DataFrame(dict_test)
print(df_test)

val_test_1 = df_test.loc[1, 'col2'] # 1
val_test_2 = df_test.loc[[1], 'col2'] # 2
val_test_3 = df_test.loc[1, ['col2']] # 3
val_test_4 = df_test.loc[[1], ['col2']] # 4

print(val_test_1)
print(type(val_test_1))
print('')

print(val_test_2)
print(type(val_test_2))
print('')

print(val_test_3)
print(type(val_test_3))
print('')

print(val_test_4)
print(type(val_test_4))



-- Result
   col1 col2  col3
0     1    a     6
1     2    b     7
2     3    c     8
3     4    d     9
4     5    e    10

b
<class 'str'>

1    b
Name: col2, dtype: object
<class 'pandas.core.series.Series'>

col2    b
Name: 1, dtype: object
<class 'pandas.core.series.Series'>

  col2
1    b
<class 'pandas.core.frame.DataFrame'>

 

위 예시를 봅시다.

val_test_1, val_test_2, val_test_3, val_test_4 모두 df_test에서 index = 1, column = 'co'2'인 값을 loc를 이용해 추출해내고 있습니다.

 

다른 점은 loc내에서 index, column을 명시할 때 대괄호를 사용하여 list의 형태로 전달하는지 여부입니다.

 

이 예시들을 하나씩 봐봅시다.

 

 

 

1. index, column 모두 상수로 전달한 경우

val_test_1 = df_test.loc[1, 'col2'] # 1


-- Result
b
<class 'str'>

 

위 예시에서처럼 loc 내에서 index, column을 모두 list가 아닌 상수로 전달한 경우는

return값이 상수입니다.

여기서는 string형태의 b가 반환됩니다.

 

 

 

 

2. index는 list로, column은 상수로 전달한 경우

3. index는 상수로, column은 list로 전달한 경우

val_test_2 = df_test.loc[[1], 'col2'] # 2
val_test_3 = df_test.loc[1, ['col2']] # 3


-- Result
1    b
Name: col2, dtype: object
<class 'pandas.core.series.Series'>

col2    b
Name: 1, dtype: object
<class 'pandas.core.series.Series'>

 

index나 column명 중 하나를 list로 전달하는 경우 Series가 return됩니다.

 

또한 이 Series의 index는 list의 형태로 전달한 것으로 나타내어집니다.

 

2번의 경우 index를 list로([1]) 전달했으므로 Series의 index도 index인 1이 됩니다.

3번의 경우 column을 list로(['col1']) 전달했으므로 Series의 index도 column인 col1이 됩니다.

 

 

 

 

 

4. index, column 모두 list로 전달한 경우

val_test_4 = df_test.loc[[1], ['col2']] # 4


-- Result
  col2
1    b
<class 'pandas.core.frame.DataFrame'>

 

위 예시에서처럼 loc 내에서 index, column을 모두 list로 전달한 경우는

DataFrame이 return됩니다.

 

 

 

 

 

 


 

 

 

 

 

만약 아래 예시처럼 column name을 명시하지 않는다면 row name이 2, 3인 모든 column이 추출됩니다.

 
df_name = df_name.loc[[2, 3]]

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
2        3         c       1
3        4         d       0
<class 'pandas.core.frame.DataFrame'>
  

 

 

 

 

 

참고로 모든 컬럼을 의미하는 콜론(:)을 loc의 컬럼위치에 적어도 동일한 결과가 나옵니다.

 
df_name = df_name.loc[[2, 3], :]

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
2        3         c       1
3        4         d       0
<class 'pandas.core.frame.DataFrame'>
  

 

 

 

 

그렇다면 어떤 컬럼 전체를 추출하고싶으면 row name을 생략하여 아래처럼 적으면 될까요?

 

아래 예시의 결과로부터 알 수 있듯이 에러가 발생합니다.

 
df_name = df_name.loc[['item_name', 'item_id']]

print(df_name)
print(type(df_name))
 
- Output
KeyError: "None of [Index(['item_name'], dtype='object')] are in the [index]"
  

 

 

 

 

 

 

만약 어떤 컬럼의 모든 행을 추출하고싶으면 아래처럼 모든 행을 의미하는 콜론(:)을 row name 부분에 적어줘야합니다.

만약 이를 생략할 경우 ['item_name', 'item_id']이것이 row name으로 인식되어 위의 예시처럼 에러가 떴던 것입니다.

또한 눈치채셨을지 모르겠지만 원본 데이터의 컬럼은 item_id 다음에 item_name 컬럼이 존재했는데loc를 이용하여 DataFrame의 일부를 추출할 땐 원본 DataFrame의 컬럼 순서는 상관없이 적어도 됩니다.

 
df_name = df_name.loc[:, ['item_name', 'item_id']]

print(df_name)
print(type(df_name))
 
- Output
  item_name  item_id
0         a        1
1         b        2
2         c        3
3         d        4
4         e        5
5         f        6
6         g        7
7         h        8
8         i        9
9         j       10
<class 'pandas.core.frame.DataFrame'>
  




 

 

 


 

 

 

 

 

아래 예시는 loc를 이용한 응용입니다.

item_id와 item_name 컬럼의 데이터에 대해서 내가 원하는 함수를 apply 또는 applymap을 이용하여 적용시킨 후 원본 DataFrame에 업데이트 시킬 수 있습니다.

이렇게 loc를 이용하면 내가 원하는 컬럼만 선택하여 해당 컬럼에 원하는 로직을 적용시킨 후, 이 새로운 데이터를 기존 DataFrame에 덮어씌울 수 있습니다.

import pandas as pd
df_name.loc[:, ['item_id', 'item_name']] = df_name.loc[:, ['item_id', 'item_name']].applymap(lambda x: str(x) + '_test')
df_name.loc[:, 'status'] = df_name.loc[:, 'status'].apply(lambda x: x * 2)

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
0   1_test    a_test       2
1   2_test    b_test       2
2   3_test    c_test       2
3   4_test    d_test       0
4   5_test    e_test       0
5   6_test    f_test       2
6   7_test    g_test       0
7   8_test    h_test       2
8   9_test    i_test       2
9  10_test    j_test       2
<class 'pandas.core.frame.DataFrame'>
  




 

 

 

또한 loc의 row 부분에 조건을 걸어서 내가 원하는 값만 추출할 수 있습니다.

import pandas as pd

c = (df_name['item_name'] == 'a') | (df_name['item_name'] == 'c')
df_new = df_name.loc[c, ['item_id']]

print(df_new)
print(type(df_new))
 
- Output
   item_id
0        1
2        3
<class 'pandas.core.frame.DataFrame'>
  




 

 

loc의 row 부분에 조건을 걸어서 내가 원하는 행의 값을 변화시킬 수도 있습니다.

import pandas as pd

c = (df_name['item_name'] == 'a') | (df_name['item_name'] == 'c')
df_name.loc[c, 'item_id'] = df_name.loc[c, 'item_id'] * 200

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
0      200         a       1
1        2         b       1
2      600         c       1
3        4         d       0
4        5         e       0
5        6         f       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
<class 'pandas.core.frame.DataFrame'>
  

결과를 보면 item_ame이 'a'이거나 'c'인 행의 item_id가 기존의 200배가 된 걸 볼 수 있죠.

조건에서 쓰인 | 이 기호는 or 이라고 생각하면 됩니다.만약 and 조건을 쓰고싶다면 & 기호를 사용하면 됩니다.

 

 

 

 

 

import pandas as pd

c = (df_name['item_name'] == 'a') | (df_name['item_name'] == 'c')
df_name.loc[c, 'item_id'] = 1000

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
0     1000         a       1
1        2         b       1
2     1000         c       1
3        4         d       0
4        5         e       0
5        6         f       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
<class 'pandas.core.frame.DataFrame'>
  

위처럼 filtering 후 filter에 걸린 부분에만 특정 값을 삽입시킬 수 있습니다.

 

 

 

 

 

import pandas as pd

c = (df_name['item_name'] == 'a') | (df_name['item_name'] == 'c')
df_name.loc[c, 'item_id'] = df_name.loc[c, 'item_name']

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
0        a         a       1
1        2         b       1
2        c         c       1
3        4         d       0
4        5         e       0
5        6         f       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
<class 'pandas.core.frame.DataFrame'>
  

위 예시는 filtering 후 item_name 컬럼의 값을 item_id 컬럼에 insert하는 예시입니다.

결과를 보면 item_name이 a, c인 행의 item_id에 item_name값이 삽입되어 a, c 값을 나타내고있죠.

 

여기서 한 가지 주의할 점이 아래 코드에 있습니다.

import pandas as pd

c = (df_name['item_name'] == 'a') | (df_name['item_name'] == 'c')
df_name.loc[c, 'item_id'] = df_name.loc[c, 'item_name']     # 1 --> 정상
df_name.loc[c, ['item_id']] = df_name.loc[c, 'item_name']   # 2 --> 정상
df_name.loc[c, ['item_id']] = df_name.loc[c, ['item_name']] # 3 --> Error 발생

위처럼 서로 다른 컬럼의 data를 insert할 경우,


1번, 2번처럼 insert할 값을 가진 컬럼(등호 기준 오른쪽)은 대괄호를 쓰지 않은 채로 명시하여 Series의 형태로 insert해야합니다.

 

(insert되는 왼쪽의 loc는 'item_id'처럼 컬럼명만 명시하건 ['item_id'] 처럼 대괄호를 사용하여 명시하건 상관없습니다.

이렇게 column 간의 insert시에는 그냥 간단하게 왼쪽, 오른쪽 모두 대괄호를 명시하지 않고 Series끼리의 insert로 기억하는게 좋습니다.)

 

3번 처럼 df_name.loc[c, ['item_name']] 컬럼 이름에 대괄호를 써서 insert할 값이 dataframe이 되면 insert가 제대로 되지 않고 NaN값이 insert됩니다.

아래는 3번처럼 했을 때의 결과입니다.

   item_id item_name  status
0      NaN         a       1
1      2.0         b       1
2      NaN         c       1
3      4.0         d       0
4      5.0         e       0
5      6.0         f       1
6      7.0         g       0
7      8.0         h       1
8      9.0         i       1
9     10.0         j       1

 

 

 

 

컬럼간 insert를 이용하면 아래처럼 insert하기 전에 어떤 값의 조절을 한 후(또는 어떤 함수를 적용시킨 후) insert하는 것도 가능합니다.

(아래 예시에서도 마찬가지로 양쪽 dataframe의 column이름에 대괄호를 쓰지 않고 Series간의 insert로 하였습니다.)

import pandas as pd

c = (df_name['item_name'] == 'a') | (df_name['item_name'] == 'c')
df_name.loc[c, 'item_id'] = df_name.loc[c, 'item_name'] + 'apple'

print(df_name)
print(type(df_name))
 
- Output
  item_id item_name  status
0  aapple         a       1
1       2         b       1
2  capple         c       1
3       4         d       0
4       5         e       0
5       6         f       1
6       7         g       0
7       8         h       1
8       9         i       1
9      10         j       1
<class 'pandas.core.frame.DataFrame'>
  

 

 

 

 

 


 

 

 

 

 

 

여기서 한 가지 더 알아봅시다.

import pandas as pd

dict_main = {
    'col1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'col2': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'valid': ['Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'Y', 'N', 'Y']
}
df_main = pd.DataFrame(dict_main)
print('df_main')
print(df_main)

dict_sub = {
    'col1': [1, 4, 5, 6, 7, 10],
    'col2': ['o', 'p', 'q', 'r', 's', 't']
}
df_sub = pd.DataFrame(dict_sub)
print('df_sub')
print(df_sub)

con = (df_main['valid'] == 'Y')
df_main.loc[con, 'col2'] = df_sub.loc[:, 'col2']
print('new')
print(df_main)


 
-- Output
df_main
   col1 col2 valid
0     1    a     Y
1     2    b     N
2     3    c     N
3     4    d     Y
4     5    e     Y
5     6    f     Y
6     7    g     N
7     8    h     Y
8     9    i     N
9    10    j     Y 

df_sub
   col1 col2
0     1    o
1     4    p
2     5    q
3     6    r
4     7    s
5    10    t 

new
   col1 col2 valid
0     1    o     Y
1     2    b     N
2     3    c     N
3     4    r     Y
4     5    s     Y
5     6    t     Y
6     7    g     N
7     8  NaN     Y
8     9    i     N
9    10  NaN     Y

위 예시는 df_main과 df_sub을 만들고, df_main에 loc를 적용시켜 df_sub의 데이터를 overwrite(덮어씌우기)하고 있습니다.

 

보시면 df_main에서 valid == 'Y'인 행에 대해서만 삽입을 진행하네요.

 

 

이 부분을 좀 더 간단하게 나타내보겠습니다..

- valid = 'Y'인 df_main
   col1 col2 valid
0     1    a     Y
3     4    d     Y
4     5    e     Y
5     6    f     Y
7     8    h     Y
9    10    j     Y 

- df_sub
   col1 col2
0     1    o
1     4    p
2     5    q
3     6    r
4     7    s
5    10    t

일단 위쪽 DataFrame은 valid = 'Y'인 행만 추출한 df_main의 모습입니다.

이 상태의 df_main의 col2에 df_sub의 col2 데이터를 넣겠다는 얘기죠.

 

보시면 총 행의 개수는 valid = 'Y'인 df_main에서 6개, df_sub에서도 6개입니다.

그러면 이것이 잘 삽입이 되어서 아래처럼 될거같습니다.

- valid = 'Y'인 df_main
   col1 col2 valid
0     1    o     Y
3     4    p     Y
4     5    q     Y
5     6    r     Y
7     8    s     Y
9    10    t     Y 

- df_sub
   col1 col2
0     1    o
1     4    p
2     5    q
3     6    r
4     7    s
5    10    t

valid = 'Y'인 df_main의 col2가 다 df_sub의 col2 데이터로 변경되었습니다.

 

- df_main
   col1 col2 valid
0     1    o     Y
1     2    b     N
2     3    c     N
3     4    p     Y
4     5    q     Y
5     6    r     Y
6     7    g     N
7     8    s     Y
8     9    i     N
9    10    t     Y

그리고 df_main의 최종적인 모습은 filtering된 부분의 col2가 모두 df_sub의 col2 데이터로 덮어씌워진 모습이라고 예상할겁니다.

 

하지만 실제 결과를 다시 보면 그렇지 않습니다.

 

 

- df_main
   col1 col2 valid
0     1    o     Y
1     2    b     N
2     3    c     N
3     4    r     Y
4     5    s     Y
5     6    t     Y
6     7    g     N
7     8  NaN     Y
8     9    i     N
9    10  NaN     Y

이렇게 NaN값이 있습니다.

그리고 index = 3인 행은 p가 아니라 r이라는 값이 들어가있죠.

 

왜그런걸까요?

그 이유는 바로 loc의 속성때문인데, loc를 이용하여 필터링이 된 상태인 DataFrame에 어떤 다른 Series나 별도의 DataFrame의 컬럼 값을 집어넣을 때에는 반드시 동일한 index끼리 overwrite을 하게되기 때문입니다.

(약간 index를 기준으로 right join을 한다고 하면 이해가 좀 더 빠를겁니다.)

 

 

 

 

 

이런 특성을 단적으로 보여주는 예시를 잠깐 보고갑시다.

import pandas as pd

dict_main = {
    'col1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'col2': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'valid': ['Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'Y', 'N', 'Y']
}
df_main = pd.DataFrame(dict_main)
print('df_main')
print(df_main)

dict_sub = {
    'col1': [1, 4, 5, 6, 7, 10],
    'col2': ['z', 'z', 'z', 'z', 'z', 'z']
}
df_sub = pd.DataFrame(dict_sub)

list_new_index = [2, 5, 6, 8, 9, 12]
df_sub = df_sub.set_index(keys=[list_new_index], drop=True, inplace=False)
print('df_sub')
print(df_sub)

df_main.loc[:, 'col2'] = df_sub.loc[:, 'col2']
print(df_main)




-- Output
df_main
   col1 col2 valid
0     1    a     Y
1     2    b     N
2     3    c     N
3     4    d     Y
4     5    e     Y
5     6    f     Y
6     7    g     N
7     8    h     Y
8     9    i     N
9    10    j     Y

df_sub
    col1 col2
2      1    z
5      4    z
6      5    z
8      6    z
9      7    z
12    10    z

   col1 col2 valid
0     1  NaN     Y
1     2  NaN     N
2     3    z     N
3     4  NaN     Y
4     5  NaN     Y
5     6    z     Y
6     7    z     N
7     8  NaN     Y
8     9    z     N
9    10    z     Y

df_main과 df_sub을 생성했습니다.

 

list_new_index = [2, 5, 6, 8, 9, 12]
df_sub = df_sub.set_index(keys=[list_new_index], drop=True, inplace=False)

그리고 df_sub은 위 부분에서처럼 index를 2, 5, 6, 8, 9, 12로 재설정하고있습니다.

 

df_main.loc[:, 'col2'] = df_sub.loc[:, 'col2']

이 상태에서 df_main의 col2에 df_sub의 col2값을 overwrite합니다.

 

그 결과를 보면 df_main의 col2값은 df_sub과 index가 일치하는 index=2, 5, 6, 8, 9에 대해서만 z값으로 overwrite되었습니다.

그 외의 index는 df_sub에 없었던 값이므로 모두 NaN으로 표시됩니다.

 

그리고 df_sub에 index=12인 행이 있는데 이것은 df_main에 없는 index이므로 df_main에는 영향을 주지 않죠.

 

df_main을 left DataFrame, df_sub을 right DataFrame이라고 보면 right join이 무슨 뜻인지 감이 오시죠?

 

 

 

 

 

 

 

 

 

 

 

 

- valid = 'Y'인 df_main
   col1 col2 valid
0     1    a     Y
3     4    d     Y
4     5    e     Y
5     6    f     Y
7     8    h     Y
9    10    j     Y 

- df_sub
   col1 col2
0     1    o
1     4    p
2     5    q
3     6    r
4     7    s
5    10    t

따라서 위 내용을 다시 보면 loc에 의해 valid='Y'인 행만 보이도록 filtering된 df_main은 index가 0, 3, 4, 5, 7, 9인데

df_sub은 0, 1, 2, 3, 4, 5이니까

동일한 index끼리 overwrite을 하게되면

index = 3인 df_main의 col2값 d가

index = 3인 df_sub의 col2값 r로 바뀌는겁니다.

 

그리고 index 7, 9는 df_sub에 존재하지 않는 데이터이므로 NaN값을 가지게되는것이죠.

 

즉, 가장 중요한건 loc를 이용해서 필터링을 한 후에(loc로 필터링을 하지 않았더라도) 다른 DataFrame의 값을 overwrite할 때에는 반드시 양쪽 DataFrame의 index가 동일한 행끼리만 overwrite가 진행된다는 것을 명심해야 합니다.

(약간 index를 기준으로 right join을 한다고 하면 이해가 좀 더 빠를겁니다.)

 

 

 

 

 

 

그러면 위 예시를 정상적으로 의도한대로 작동하게해봅시다.

import pandas as pd

dict_main = {
    'col1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'col2': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'valid': ['Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'Y', 'N', 'Y']
}
df_main = pd.DataFrame(dict_main)
print('df_main')
print(df_main, '\n')

con = (df_main['valid'] == 'Y')
print(df_main.loc[con, :], '\n')

dict_sub = {
    'col1': [1, 4, 5, 6, 7, 10],
    'col2': ['o', 'p', 'q', 'r', 's', 't']
}
df_sub = pd.DataFrame(dict_sub)

list_index = [0, 3, 4, 5, 7, 9]
df_sub = df_sub.set_index(keys=[list_index], drop=True, inplace=False)
print('df_sub')
print(df_sub, '\n')

con = (df_main['valid'] == 'Y')
df_main.loc[con, 'col2'] = df_sub.loc[:, 'col2']
print('new')
print(df_main)



-- Output
df_main
   col1 col2 valid
0     1    a     Y
1     2    b     N
2     3    c     N
3     4    d     Y
4     5    e     Y
5     6    f     Y
6     7    g     N
7     8    h     Y
8     9    i     N
9    10    j     Y 

   col1 col2 valid
0     1    a     Y
3     4    d     Y
4     5    e     Y
5     6    f     Y
7     8    h     Y
9    10    j     Y 

df_sub
   col1 col2
0     1    o
3     4    p
4     5    q
5     6    r
7     7    s
9    10    t 

new
   col1 col2 valid
0     1    o     Y
1     2    b     N
2     3    c     N
3     4    p     Y
4     5    q     Y
5     6    r     Y
6     7    g     N
7     8    s     Y
8     9    i     N
9    10    t     Y

코드는 거의 동일합니다만 df_sub의 index를 set_index method를 이용하여 df_sub의 index를 df_main에 overwrite되었으면 하는 index로 재설정 해주는 코드가 추가되었습니다.

그래서 filtering된 df_main과 df_sub의 index가 일치하는 것을 볼 수 있죠.

 

또한 최종 결과를 봐도 index=0, 3, 4, 5, 7, 9가 순서대로 o, p, q, r, s, t로 overwrite된 것을 볼 수 있습니다.

 

 

 

 

 

 

 

 

만약 index를 신경쓰고싶지않다고 하면 아래와같이 삽입할 값을 list로 만들어도 됩니다.

import pandas as pd

dict_main = {
    'col1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'col2': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'valid': ['Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'Y', 'N', 'Y']
}
df_main = pd.DataFrame(dict_main)
print('df_main')
print(df_main, '\n')

con = (df_main['valid'] == 'Y')
print(df_main.loc[con, :], '\n')


list_new_values = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta']
con = (df_main['valid'] == 'Y')
df_main.loc[con, 'col2'] = list_new_values
print('new')
print(df_main)



-- Output
df_main
   col1 col2 valid
0     1    a     Y
1     2    b     N
2     3    c     N
3     4    d     Y
4     5    e     Y
5     6    f     Y
6     7    g     N
7     8    h     Y
8     9    i     N
9    10    j     Y 

   col1 col2 valid
0     1    a     Y
3     4    d     Y
4     5    e     Y
5     6    f     Y
7     8    h     Y
9    10    j     Y 

new
   col1     col2 valid
0     1    alpha     Y
1     2        b     N
2     3        c     N
3     4     beta     Y
4     5    gamma     Y
5     6    delta     Y
6     7        g     N
7     8  epsilon     Y
8     9        i     N
9    10     zeta     Y

df_main에서 valid = Y인 것만 filtering한 후 filtering된 상태의 col2에 list_new_values에 있는 값을 모두 overwrite하였습니다.

 

list는 index가 없기때문에 loc filtering을 해서 index가 어떻게되건 잘 삽입된 것을 볼 수 있습니다.

 

list를 이용한 overwrite을 할 때에 주의할 점은 filtering된 dataframe의 행 개수와 list에 있는 요소의 개수가 같아야합니다.

위 예시에서 보면 filtering된 dataframe의 행 수는 index = 0, 3, 4, 5, 7, 9로 총 6개입니다.

list_new_values에 있는 요소의 개수도 총 6개이죠.

만약 행의 개수와 삽입할 값이 담긴 list에 있는 요소의 개수가 다르면 error가 발생합니다.

 

 

 

 

 

 

import pandas as pd

dict_main = {
    'col1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'col2': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'valid': ['Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'Y', 'N', 'Y']
}
df_main = pd.DataFrame(dict_main)
print('df_main')
print(df_main, '\n')

con = (df_main['valid'] == 'Y')
print(df_main.loc[con, :], '\n')

dict_sub = {
    'col1': [1, 2, 3, 4, 5, 6],
    'col2': ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta']
}
df_sub = pd.DataFrame(dict_sub)


con = (df_main['valid'] == 'Y')
df_main.loc[con, 'col2'] = list(df_sub.loc[:, 'col2'])
print('new')
print(df_main)



-- Output
df_main
   col1 col2 valid
0     1    a     Y
1     2    b     N
2     3    c     N
3     4    d     Y
4     5    e     Y
5     6    f     Y
6     7    g     N
7     8    h     Y
8     9    i     N
9    10    j     Y 

   col1 col2 valid
0     1    a     Y
3     4    d     Y
4     5    e     Y
5     6    f     Y
7     8    h     Y
9    10    j     Y 

new
   col1     col2 valid
0     1    alpha     Y
1     2        b     N
2     3        c     N
3     4     beta     Y
4     5    gamma     Y
5     6    delta     Y
6     7        g     N
7     8  epsilon     Y
8     9        i     N
9    10     zeta     Y

이걸 응용하면 df_sub의 col2 값을 list의 형태로 만들어서 df_main에 overwrite하는 방법도 있습니다.

 

이렇게하면 양쪽 dataframe간의 index를 신경안써도 된다는 장점이 있죠.

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

그렇다면 iloc는 loc와 무엇이 다를까요?

iloc는 추출할 row와 column을 무조건 index number로 표기해야 합니다.따라서 아래 코드는 에러가 뜹니다.

 
df_name = df_name.iloc[:, ['item_name', 'item_id']]

print(df_name)
print(type(df_name))
 
- Output
IndexError: .iloc requires numeric indexers, got ['item_name' 'item_id']
  

 

 

위 코드가 제대로 작동하도록 고쳐보면 아래와 같습니다.

 

df_name의 컬럼 순서는 item_id, item_name, status이었고, 순서대로 0, 1, 2라는 index number를 가집니다.

따라서 item_id, item_name 컬럼을 iloc를 이용하여 추출하고싶다면 0, 1 index number를 가진 column을 추출해야 합니다.

 
df_name = df_name.iloc[:, [0, 1]]

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name
0        1         a
1        2         b
2        3         c
3        4         d
4        5         e
5        6         f
6        7         g
7        8         h
8        9         i
9       10         j
<class 'pandas.core.frame.DataFrame'>
  




 

 

row index를 제시하여 원하는 row, 원하는 column을 추출할 수 있습니다.

 
df_name = df_name.iloc[[2, 5], [0, 1]]

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name
2        3         c
5        6         f
<class 'pandas.core.frame.DataFrame'>
  




 

 


 

 

 

 

 

loc와 iloc의 차이를 좀 더 자세히 보기 위해 test용 DataFrame을 생성합니다.

DataFrame 생성 시 index 옵션을 이용하여 index 이름을 index_list에 있는 이름으로 변경시켜 DataFrame을 생성했습니다.

import pandas as pd
dict_name = {
    'item_id': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'item_name': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'status': [1, 1, 1, 0, 0, 1, 0, 1, 1, 1]
}
index_list = ['row0', 'row1', 'row2', 'row3', 'row4', 'row5', 'row6', 'row7', 'row8', 'row9']
df_name = pd.DataFrame(dict_name, index=index_list)

print(df_name)
print(type(df_name))
 
- Output
      item_id item_name  status
row0        1         a       1
row1        2         b       1
row2        3         c       1
row3        4         d       0
row4        5         e       0
row5        6         f       1
row6        7         g       0
row7        8         h       1
row8        9         i       1
row9       10         j       1
<class 'pandas.core.frame.DataFrame'>
  




 

 

위 DataFrame에 대해서 아래 코드를 실행시키면 에러가 뜹니다.

 

맨 처음에 봤던 예시 DataFrame은 index를 따로 지정하지 않아서 row name이 모두 0부터 시작하는 정수의 형태로 자동 생성이 되었기 때문에 이 자동생성된 이름을 이용하여 추출 할 수 있었습니다.

 

그러나 이제는 index name이 0부터 시작하는 정수가 아니라 row0, row1, ... 등의 문자이기 때문에 이 문자대로 입력을 해줘야 아래 두 번째 예시처럼 에러가 나지 않습니다.

df_name = df_name.loc[[2, 3], ['item_name', 'item_id']]

print(df_name)
print(type(df_name))
 
- Output
KeyError: "None of [Int64Index([2, 3], dtype='int64')] are in the [index]"
  

 

df_name = df_name.loc[['row2', 'row3'], ['item_name', 'item_id']]

print(df_name)
print(type(df_name))
 
- Output
     item_name  item_id
row2         c        3
row3         d        4
<class 'pandas.core.frame.DataFrame'>
  




 

 

위 예시에 대해 loc 대신 iloc를 사용한다면 당연히 아래처럼 에러가 뜹니다.

 

iloc에선 row, column모두 index number를 인자로서 전달해야하는데 row name을 전달했기 때문이죠.

df_name = df_name.iloc[['row2', 'row3'], [0, 1]]

print(df_name)
print(type(df_name))
 
- Output
KeyError: "None of [Int64Index([0, 1], dtype='int64')] are in the [columns]"
  

 

 

이 경우엔 아래처럼 row name대신 row index를 전달하면 됩니다.

df_name = df_name.iloc[[2, 3], [0, 1]]

print(df_name)
print(type(df_name))
 
- Output
      item_id item_name
row2        3         c
row3        4         d
<class 'pandas.core.frame.DataFrame'>
  



 

 

 

 

 

728x90
반응형
Comments