PL/Python

pandas Dataframe, Series 차이점 정리 (데이터프레임, 시리즈)

PIYA 2022. 10. 31.

포스팅 목적

열심히 공부하던 찰나, 가장 기본이 되는 데이터프레임과 시리즈가 무엇이고 무슨 차이가 있는지

확실하게 설명하기 어렵다는 생각이 들었다.

역시 기초가 제일 중요하다..

 

시리즈와 데이터프레임

차이점을 위주로 코드 스터디를 해보자!

 

첫번째: 기본적인 차이점

가장 근본적인 차이는 칼럼갯수에 있다.

시리즈는 칼럼이 1개, 데이터프레임은 1개 이상이다.

import pandas as pd

# 간단하게 길이 4짜리 리스트를 만들었다
ldata = [1,2,3,4]

# 시리즈
ser = pd.Series(ldata)
print('[시리즈의 경우]')
print(ser)
print('shape:', ser.shape, '\n')

# 데이터프레임
df = pd.DataFrame(ldata)
print('[데이터프레임의 경우]')
print(df)
print('shape:', df.shape)

 

4개의 원소가 있는 리스트 데이터 ldata를 선언하여 각각 시리즈와 데이터프레임을 생성했다.

차이점을 보면, 시리즈는 칼럼명이 나오지 않고, 데이터프레임은 칼럼명 0가 붙었다.

 

 

두번째: 딕셔너리로 시리즈와 데이터프레임을 만들면?

이번엔 아래와 같은 동일한 딕셔너리를 가지고 각각 데이터프레임과 시리즈를 만들어보자.

딕셔너리의 키값이 하나는 str형 'a'이고, 하나는 int형 2 인것을 확인하자.

import pandas as pd

# 딕셔너리를 준비
dic = {'a':[1,2], 2:[3,4]}

# 시리즈 생성
ser = pd.Series(dic)
print('[시리즈의 경우]')
print(ser)
print('shape:', ser.shape, '\n')

# 데이터프레임 생성
df = pd.DataFrame(dic)
print('[데이터프레임의 경우]')
print(df)
print('shape:', df.shape)

시리즈와 데이터프레임의 다른 해석

칼럼명을 받지 않는 시리즈는 딕셔너리 dic의 키값을 '인덱스'로 해석한다.

그리고 뒤에 따라오는 리스트를 통째로 해당 인덱스에 저장한다.

(str형인 'a'도 인덱스가 될 수 있다)

 

데이터프레임은 dic의 키값을 '칼럼명'으로 해석하고 인덱스는 자동 넘버링된다.

리스트의 값들을 해당 키, 즉 각 칼럼에 맞게 저장한다

 

※ 위의 경우 딕셔너리 키 2개의 리스트길이가 동일했기 때문에 데이터프레임이 생성된 것이다.

아래의 사진처럼 칼럼 'a'에 들어갈 값 2개, 칼럼 'b'에 들어갈 값은 3개이면 에러가 발생한다.

 

같은 딕셔너리를 가지고 똑같이 시리즈와 데이터프레임을 만들더라도

결과물은 다를 수 있다는거!

 

 

 

세번째: 각각에 ['3'] 연산을 하면 어떻게됩니까?

이번에는 시리즈와 데이터프레임에 동일한 연산을 수행해보자

import pandas as pd

# 딕셔너리를 준비
dic = {'a':[1,2], 2:[3,4]}

# 시리즈와 데이터프레임의 생성
ser = pd.Series(dic)
df = pd.DataFrame(dic)

# 시리즈의 경우
ser['3']=4

# 데이터프레임의 경우
df['3']=4

print('-시리즈-')
print(ser, '\n')
print('-데이터프레임-')
print(df)

똑같이 ['3']=4 코드를 수행했지만, 결과가 다르다!

 

칼럼이 없는 시리즈'3'을 인덱스명으로 인식하였고,

데이터프레임'3'을 칼럼명으로 인식하여 칼럼 '3'을 추가하고 동일한 4값을 두번 추가하였다.

 

 

 

네번째: 시리즈와 익숙해지기

시리즈 인덱스에 대해서 일단 정리하자.

읽기 싫어도 천천히 한줄씩 읽어보기..!

# 시리즈 데이터 준비
dic = {'a':[1,2], 'b':3, 'c': [4,5,6]}
ser = pd.Series(dic)
print('-series-')
print(ser, '\n')

# 시리즈의 인덱스 프로퍼티 구경하기
print('1. ser.index:', ser.index, '\n')

# 시리즈의 인덱스 선택하기
print('2. ser[\'b\']:', ser['b'], '\n')

# 시리즈의 순서상 0번째 데이터 선택하기
print('3. iloc[0]:', ser.iloc[0], '\n')

# ser.loc[0]의 경우 ser은 [0]을 인덱스로 인식한다.
# 위 ser 시리즈에서는 0이라는 인덱스가 없기 때문에 KeyError 발생.
print('4. ser.loc[0]:', ser.loc[0])

4번은 키에러 발생으로 3번까지만 출력되었다.

 

 

 

다섯번째: 데이터프레임과 익숙해지기

데이터프레임도 예제를 가지고 인덱스 관련해서 간단하게 한번 정리해보자.

# 예제 데이터프레임 생성
dic = {'a-col':['a-val1','a-val2','a-val3'],
      'b-col':['b-val1','b-val2','b-val3'],
      'c': [11,22,33]}
idx = ['a', 'b', 3]

# 데이터프레임 생성시 칼럼값은 columns 인자에 넣어주고,
# 인덱스값 설정은 index 인자에 넣어준다.
df = pd.DataFrame(dic, index = idx)

# iloc[n] 사용시 순서상 n번째 데이터값을 반환한다.
# 이는 iloc[]의 정의에 의한 것이므로 시리즈와 동일하게 해석할 수 있지만,
# 데이터프레임은 해당 n번째 행데이터들에 해당하는 컬럼값도 같이 반환해야 하므로,
# 반환값은 '시리즈'형태가 된다.
print('-- 1번 df.iloc[1]--')
print(df.iloc[1])
print('type:', type(df.iloc[1]))
print('기존 컬럼값이 인덱스값으로 변환되어 들어감:', df.iloc[1].index, '\n')

# 시리즈의 경우 ser['z']시 인덱스'z'를 의믜하지만,
# 데이터프레임에서 ['z']는 컬럼명 ['z']를 의미한다.
# 이때, 해당 칼럼의 열데이터를 뽑아오는데 기존 인덱스를 보존하여 시리즈형으로 반환한다.
print('-- 2번 df[\'b-col\']--')
print(df['b-col'])
print('type:', type(df['b-col']))

df

df가 어떻게 생겼는지는 맨 아래에 표로 확인할 수 있다.

주석을 굉장히 자세히 달았다.

 

 

 

잡스럽지만 신기한 점

신기한 걸 발견해서 코드로 정리해본다.

#시리즈 생성
ser = pd.Series([10,20], index=['a','b'])
print('-시리즈 생긴거 확인1-')
print(ser, '\n')

# 정수형 인덱스가 없을때는 ser[0] 사용가능
print('ser[0] 사용가능')
print(ser[0], '\n')

# 정수형 인덱스를 추가한 후에는 ser[0] 키에러 발생
ser = pd.Series([10,20,30], index=['a','b',3])
print('-시리즈 생긴거 확인2-')
print(ser, '\n')

print('ser[0] 사용불가')
print(ser[0])

마지막 ser[0]은 키에러 발생

 

시리즈에 인덱스가 'a', 'b' 처럼 str형만 있는 경우에는,

ser[0]을 했을시 0이 숫자를 의미하므로 ser.iloc[0]과 같이 자동적으로 동작한다.

하지만, 시리즈에 int형 인덱스 3이 추가되자 ser[0]을 호출할 시 키에러가 난다.

 

즉 ser[n]과 같은 코드는 굉장히 모호하게 해석된다는 거다.

파이썬이니까 자동적으로 바꿔주지, 결코 권장할만한 코드작성방법이 아니다.

 

*(이것도 시리즈에서나 통하는거지, 데이터프레임에서는 칼럼명이 str으로만 이루어져 있어도 df[0]과 같은 접근은 불허한다.)

 

 

Conclusion

이것저것 너무 많이 정리해서 정신이 없는데, 꽤나 도움이 되는 코드공부였다.

 

 

댓글