돈이 만드는 세상

#3-1 파이썬 pandas 초급 강의(수정 필요) 본문

프로그래밍/Python

#3-1 파이썬 pandas 초급 강의(수정 필요)

피델리오 2022. 2. 15. 18:32

ndarray dtype

import numpy as np

a = np.array([1, 2, 3])

type(a) # numpy.ndarray

# indexing이 list와 같음
a[0]

numpy는 기본적으로 vector를 사용합니다. 추가적으로 .append() 같은 함수가 없습니다. 즉, python의 list와는 다른 data type입니다.

a = [1, 2, 3]
b = [4, 5, 6]

new_list = []

for e1, e2 in zip(a, b):
    new_list.append(e1 + e2)

new_list # 5, 7, 9
# !=
a + b # 1, 2, 3, 4, 5, 6

O(n)의 시간복잡도가 걸릴 수 밖에 없습니다.

universal function
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# 길이가 길어도 근사적으로 1초가 걸린다.
a + b # 5, 7, 9

np.sum
np.abs
np.log
np.exp
np.isnan
a = np.array(
    [
        [1, 2],
        [3, 4],
    ]
)

a[0][1] # 2
a + a
# ([[2, 4], [6, 8]])

현재까지 형태를 이해한 바로는 행렬의 형태를 가지고 있다고 생각하면 쉽습니다.

Series Data Type

  • Numpy's ndarray + 숫자가 아닌 다른 type의 index (E.g. 문자열)
import pandas as pd

# Series라는 클래스가 존재
a = pd.Series([1, 2, 3, 4])
print(a)

# 첫번째 방법
s2 = pd.Series(
    [1, 2, 3, 4],
    index=['a', 'b', 'c', 'd']
)
print(s2.head(3))

# 두번째 방법
s3 = pd.Series(
    {
        'a' : 1,
        'b' : 2,
        'c' : 3,
        'd' : 4,
    }
)
print(s3.head())

0 1
1 2
2 3
3 4
dtype: int64
a 1
b 2
c 3
dtype: int64
a 1
b 2
c 3
d 4
dtype: int64

Cython을 기반에 두고 있기 때문에 int64라는 데이터 타입을 갖게 됩니다. 병렬 연산을 위해 한가지 data type만 가지고 있을 수 있습니다.

Nan과 관련된 함수
np.nan
s = pd.Series([1, 2, 3, 4, 5, 6, 7, 8, 9, nd.nan])
print(s.head())

0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
dtype: float64

float 자료형으로 보는 이유는 Pandas에서 기본적으로 nan이라는 값자체를 float로 보는 것으로 정했기 때문입니다.(1.0.0 이후에는 pd.NA라는 생겼지만 아직 실험단계라고 합니다.) 이로 인해, nan이 하나라도 들어가 있으면, 해당 컬럼을 float data type으로 자동 변환하게 됩니다.

len(s) # 10
s.shape # (10,) - 행렬 형태를 결과값으로 리턴하는 것 같습니다.
s.count() # 9
s.unique() # set()과 같이 중복된 데이터 셋을 가지더라도 유니크한 데이터만 볼 수 있게 해줌
s.value_counts() # 내가 넣은 값들에 대해 몇 개씩 들어있는지 'Series' 형태로 return함.

Series 내부에서는 index가 지들끼리 align이 되어서 같은 인덱스에 해당하는 값들끼리 연산됩니다.

a1 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
a2 = pd.Series([4, 3, 2, 1], index=['d', 'c', 'b', 'a'])
a3 = a1 + a2
print(a3.head())

a 2
b 4
c 6
d 8
dtype: int64

DataFrame 데이터 타입

다수의 Series를 하나의 변수로 관리할 수 있도록 만든 자료형입니다.

  • Seriesdict 형태라고 보면 됩니다. { 컬럼명1 : Series1, 컬럼명 2 : Series2}
  • 각 Series는 DataFrame의 column을 이룸
  • 당연히 DataFrame을 이루는 Series간의 index는 서로 다 같음! => 동일 index 사용
import pandas as pd
import numpy as np

s1 = np.arange(1, 6, 1)
s2 = np.arange(6, 11, 1)

d1 = pd.DataFrame(
    {
        'c1' : s1,
        'c2' : s2,
    }
)

print(d1)

c1 c2
0 1 6
1 2 7
2 3 8
3 4 9
4 5 10

dict의 형태로 DataFrame을 보편적인 방법이지만, 다른 방법드도 있습니다.

import pandas as pd
import numpy as np

s1 = np.arange(1, 6, 1)
s2 = np.arange(6, 11, 1)

d1 = pd.DataFrame(
    {
        'c1' : s1,
        'c2' : s2,
    }
)

print(d1)

# 1번째 방법
d2 = pd.DataFrame(
    [
        [10, 11],
        [10, 12]
    ]
)

d3 = pd.DataFrame(
    np.array(
        [
            [10, 11],
            [20, 21]
        ]
    )
)

# 2번째 방법 (많이 안쓰임 주의)
d4 = pd.DataFrame(
    [
        pd.Series(np.arange(10, 15)),
        pd.Series(np.arange(15, 20)),
    ],
    index=['a1', 'a2']
)

print(d2)
print(d3)
print(d4)

c1 c2
0 1 6
1 2 7
2 3 8
3 4 9
4 5 10
0 1
0 10 11
1 10 12
0 1
0 10 11
1 20 21
0 1 2 3 4
a1 10 11 12 13 14
a2 15 16 17 18 19

만약 해당하는 인덱스가 없으면 어떻게 될 것인가에 대해서 코드를 실행해보겠습니다.

n1 = pd.Series(np.arange(1, 5, 1), index=['a', 'b', 'c', 'd'])
n2 = pd.Series(np.arange(5, 9, 1), index=['e', 'f', 'c', 'a'])

d5 = pd.DataFrame(
    {
        'c1' : n1,
        'c2' : n2,
    }
)

print(d5)

new_n3 = pd.Series(np.arange(1, 3, 1), index=['e', 'f'])
d5['c3'] = new_n3

print(d5)

c1 c2
a 1.0 8.0
b 2.0 NaN
c 3.0 7.0
d 4.0 NaN
e NaN 5.0
f NaN 6.0
c1 c2 c3
a 1.0 8.0 NaN
b 2.0 NaN NaN
c 3.0 7.0 NaN
d 4.0 NaN NaN
e NaN 5.0 1.0
f NaN 6.0 2.0

Index 관련

Reindexing

  • 새로운 index label을 기반으로 기존의 "index-value" mapping은 유지한채 재배열하는 것

참고: index 자체를 바꾸는 것("index-value" mapping이 깨짐)

ss = pd.Series([1, 2, 3, 4, 5])
print(ss)

ss.index = ['a', 'b', 'c', 'd', 'e']
print(ss)

0 1
1 2
2 3
3 4
4 5
dtype: int64
a 1
b 2
c 3
d 4
e 5
dtype: int64

참고 : set_index() : 특정 column을 index로 만듦

df['c5'] = pd.Series([1, 2, 3, 4, 5, 6], index=[0, 1, 2, 3, 4, 10])
df.set_index("c5")

set_index()를 통해 column에 있는 value들을 index의 name으로써 사용할 수 있습니다. 해당 메서드를 사용하게 되면 index의 name으로 쓰게 된 column은 지워집니다.

s2 = ss.reindex(
    ['a', 'b', 'c', 'd']
)
print(s2)
# a    1.0
# c    3.0
# e    5.0
# g    NaN
# dtype: float64
s2['a'] = 0
# a    0.0
# c    3.0
# e    5.0
# g    NaN
# dtype: float64
# 이렇게 하면 안됨
s1 = pd.Series([0, 1, 2], index=[0, 1, 2])
s2 = pd.Series([3, 4, 5], index=['0', '1', '2'])
s1
s2
# 0    0
# 1    1
# 2    2
# dtype: int64
# 0    3
# 1    4
# 2    5
# dtype: int64
s1 + s2
# 0   NaN
# 1   NaN
# 2   NaN
# 0   NaN
# 1   NaN
# 2   NaN
# dtype: float64
s2 = s2.reindex(s1.index)
# 0   NaN
# 1   NaN
# 2   NaN
# dtype: float64
# 첫번째 방법
s2.index = s2.index.astype(int)
print(s1 + s2)
# 0    3
# 1    5
# 2    7
# dtype: int64
# 두번째 방법
s2 = s2.reindex(['a', 'f'], fill_value=0) # fill 0 instead of Nan
print(s2)
# a 0
# f 0
s3 = pd.Series(['red', 'green', 'blue'], index=[0, 3, 5])
print(s3)

s3 = s3.reindex(np.arange(0, 7), method='ffill')
print(s3)
# 0      red
# 3    green
# 5     blue
# dtype: object
# 0      red
# 1      red
# 2      red
# 3    green
# 4    green
# 5     blue
# 6     blue
# dtype: object

reindex() 예제

# 삼성전자
df1 = fdr.DataReader("005930", "2018-01-02", "2018-10-30")
# KODEX 200 (ETF)
df2 = fdr.DataReader("069500", "2018-01-02", "2018-10-30")

df2 = df2.drop(pd.to_datetime("2018-01-03"))

new_df2 = df2.reindex(df1.index)
print(new_df2.fillna(method="ffill"))
print(new_df2.head())
           Open     High      Low    Close      Volume    Change

Date
2018-01-02 30245.0 30334.0 30150.0 30267.0 5016257.0 0.004414
2018-01-03 30245.0 30334.0 30150.0 30267.0 5016257.0 0.004414
2018-01-04 30567.0 30582.0 30145.0 30156.0 8914121.0 -0.007896
2018-01-05 30232.0 30558.0 30232.0 30566.0 8121543.0 0.013596
2018-01-08 30687.0 30815.0 30517.0 30771.0 8023240.0 0.006707
... ... ... ... ... ... ...
2018-10-24 25722.0 25744.0 25415.0 25462.0 12097024.0 -0.005157
2018-10-25 24930.0 25065.0 24689.0 25056.0 11679604.0 -0.015945
2018-10-26 25079.0 25084.0 24433.0 24658.0 8230431.0 -0.015884
2018-10-29 24742.0 24906.0 24410.0 24459.0 5301352.0 -0.008070
2018-10-30 24424.0 24847.0 24336.0 24628.0 8010749.0 0.006910

[202 rows x 6 columns]
Open High Low Close Volume Change
Date
2018-01-02 30245.0 30334.0 30150.0 30267.0 5016257.0 0.004414
2018-01-03 NaN NaN NaN NaN NaN NaN
2018-01-04 30567.0 30582.0 30145.0 30156.0 8914121.0 -0.007896
2018-01-05 30232.0 30558.0 30232.0 30566.0 8121543.0 0.013596
2018-01-08 30687.0 30815.0 30517.0 30771.0 8023240.0 0.006707