Pandas DataFrame은 행과 열이 있는 데이터를 빠르게 만들고, 선택하고, 이동하고, rolling 연산을 적용하고, axis 방향으로 집계할 수 있게 해 주는 Python 데이터 분석 도구다.
이 글은 1차원 리스트와 2차원 dict를 DataFrame으로 바꾸며 컬럼과 인덱스 감각을 잡고, shift, rolling, apply, axis, transpose를 직접 실험한 메모다.
핵심 정리
DataFrame을 처음 쓸 때는 행과 열의 방향을 먼저 익히는 것이 중요하다. 리스트 하나를 DataFrame으로 만들면 기본 컬럼이 생기고, dict 안에 dict가 들어 있는 형태를 넣으면 바깥 키가 컬럼, 안쪽 키가 인덱스처럼 해석될 수 있다. shift는 데이터를 행 방향으로 밀어 이전 값이나 다음 값을 비교할 때 유용하고, rolling은 일정한 창 크기 안에서 합계나 사용자 정의 연산을 적용할 때 사용한다. apply는 각 컬럼이나 행에 함수를 적용하는 도구인데, axis 값에 따라 세로 방향과 가로 방향의 연산이 달라진다. transpose는 행과 열을 서로 바꿔 데이터 구조를 다시 보는 데 도움이 된다. 원문처럼 list가 element로 들어간 복잡한 구조는 깔끔해 보여도 실제 연산에서는 다루기 어려울 수 있으므로, 분석 목적에 맞게 2차원 표 형태로 풀어내는 것이 중요하다.
- DataFrame은 행과 열이 있는 표 형태 데이터를 다루는 기본 구조다.
- 1차원 리스트로 만들면 기본 컬럼이 자동으로 생긴다.
- 2차원 dict는 바깥 키와 안쪽 키가 컬럼과 인덱스로 해석될 수 있다.
- shift는 행을 앞뒤로 밀어 이전 값이나 다음 값을 비교할 때 유용하다.
- rolling은 일정 창 크기 안에서 합계나 사용자 정의 계산을 적용한다.
- apply는 컬럼이나 행 단위로 함수를 적용할 때 사용한다.
- axis는 연산 방향을 정하므로 결과 모양에 직접 영향을 준다.
- transpose는 행과 열을 바꿔 데이터 방향을 다시 볼 때 사용한다.
원문은 Pandas를 처음 써 보며 DataFrame이 루프보다 편하다고 느낀 지점을 실험한 기록입니다. 보강문에서는 생성, shift, rolling, apply, axis, transpose의 역할을 먼저 정리했습니다. DataFrame을 잘 쓰려면 개별 함수보다 데이터가 행 기준인지 열 기준인지, 연산 결과가 차원을 유지하는지 줄이는지를 계속 확인하는 습관이 중요합니다.
이어서 볼 글
- NumPy 배열 사용법: 난수 생성, 조건 인덱싱, np.where - 열 단위 연산과 조건 선택의 배열 기반을 먼저 확인할 수 있다.
이 링크도 좋은듯 (10min to pandas)
왜 필요하나 싶었는데 보면 볼수록 깜짝놀램...
프로토타이핑에 너무 좋은거 같고.. 같은 의미에서 python과의 궁합도 최고인듯!
z-score 만지다 보면 다차원 배열에 대해서 복잡한 연산이 필요하고 이걸 나는 자연스럽게 루프를 통해서 구현했는데..
pandas DataFrame을 보니까 그냥 함수콜 몇 번 만으로 해결되는거 보고 "아~ 이래서 쓰는구나" 싶어하는 중
1차원
접근은 [col_index][row_index]로 하네..
>>> import pandas as pd
>>> a = [1,2,3]
>>> df_a = pd.DataFrame(a)
>>> df_a[0][0]
1
>>> df_a[0][1]
2
>>> df_a[0][2]
3
1차원인데도 컬럼 0이 강제 할당되네..
2차원
2차원 dict를 넣으면 첫번째 키가 컬럼, 두번째가 row로 할당되네
>>> a = {}
>>> a['AAPL']={}
>>> a['AAPL']['0101']=11
>>> a['AAPL']['0102']=22
>>> a['NIKE']={}
>>> a['NIKE']['0101']=33
>>> a['NIKE']['0102']=44
>>> a
{'AAPL': {'0101': 11, '0102': 22}, 'NIKE': {'0101': 33, '0102': 44}}
>>> import pandas as pd
>>> df_a = pd.DataFrame(a)
>>> df_a
AAPL NIKE
0101 11 33
0102 22 44
element가 list인 2차원 샘플
a = {'AAPL': {'0101': [1, 2, 3], '0102': [4, 5, 6]}, 'NIKE': {'0101': [7, 8, 9], '0102': [10, 11, 12]}}
3차원
음.. 아무래도.. 3차원이상은 너무 복잡해지는 거 같다.
예를들어서 아래와 같은 데이터가 있다고 하자
>>> a = {'AAPL': {'0101': [1, 2, 3], '0102': [4, 5, 6]}, 'NIKE': {'0101': [7, 8, 9], '0102': [10, 11, 12]}}
>>> da = pd.DataFrame(a)
>>> da
AAPL NIKE
0101 [1, 2, 3] [7, 8, 9]
0102 [4, 5, 6] [10, 11, 12]
이거는 element가 list인 2차원 데이터로 볼수도 있지만 리스트 자체를 추가 차원으로 보면 3차원이다.
위와 같이 깔끔해 보이는 상태로 rolling을 하고 싶었으나 잘 되지 않았다.
그렇다면 컬럼 별로 잘라서 다시 조립하는 형태로 갈 수 밖에 없을 듯 하다.
아래같이럼 하면 맘에는 안들지만 되긴함

shift
1차원
>>> import pandas as pd
>>> a = [1,2,3]
>>> df_a = pd.DataFrame(a)
>>> df_a
0
0 1
1 2
2 3
>>> df_a.shift(1)
0
0 NaN
1 1.0
2 2.0
>>> df_a.shift(-1)
0
0 2.0
1 3.0
2 NaN
>>>
2차원
>>> df_b = df_a.shift(1)
>>> df_b
AAPL NIKE
0101 NaN NaN
0102 11.0 33.0
shift는 row가 밀리는 개념이네
rolling
몇 개씩 묶어서 오버랩 스타일로 연산하고 결과를 쓰는 걸 할때..
>>> df_a
0
0 1
1 2
2 3
>>> df_a.rolling(2).sum()
0
0 NaN
1 3.0
2 5.0
0 - 1 인덱스에 있는 값 두 개 묶어서 1인덱스에 쓰고 (1 + 2 = 3)
1 - 2 인덱스에 있는 값 두 개 묶어서 2인덱스에 쓰고 (2 + 3 = 5)
하는 걸 볼 수 있다.
apply lambda
sum()같은 거 대신에 개별 element에 대한 연산을 apply를 써서 정의해 줄 수 있다.
아래처럼 하면 그냥 sum()하고 같은 거고
>>> df_a.rolling(2).apply(lambda x: sum(x))
0
0 NaN
1 3.0
2 5.0
아래처럼 개별 element에 대한 연산을 포함시킬수도 있음
>>> df_a.rolling(2).apply(lambda x: sum(x+1)+1)
0
0 NaN
1 6.0
2 8.0
아래처럼 한 번 더 연산을 적용해서 합칠수도 있음(mean()은 pandas 함수)
>>> df_a.rolling(2).apply(lambda x: sum(x+1)+1).apply(lambda x: x.mean())
0 7.0
dtype: float64
아래처럼 x라는 표현을 살리면 계산식에 x.sum()같은 aggregation 함수가 들어가도 차원이 축소하지 않고
>>> a = [1,2,3]
>>> df_a = pd.DataFrame(a)
>>> df_a
0
0 1
1 2
2 3
>>> df_a.apply(lambda x: x + 1)
0
0 2
1 3
2 4
>>> df_a.apply(lambda x: x + x.sum())
0
0 7
1 8
2 9
아래처럼 x라는 표현을 없애고 aggregation 함수만 놓으면 차원이 축소한다. (상수식을 넣어도 축소한다)
>>> df_a
0
0 1
1 2
2 3
>>> df_a.apply(lambda x: x.sum())
0 6
dtype: int64
2차원인 경우는 아래처럼 aggregation의 방향을 바꿀 수 있다(0이면 세로축으로 row간에 연산해주고, 1이면 가로축으로 col간 연산해줌)
>>> a = {'AAPL': {'0101': 11, '0103': 33, '0102': 22}, 'NIKE': {'0101': 33, '0103': 55, '0102': 44}}
>>> df_a = pd.DataFrame(a)
>>> df_a
AAPL NIKE
0101 11 33
0102 22 44
0103 33 55
>>> df_a.apply(lambda x: x/x-1+x.sum())
AAPL NIKE
0101 66.0 132.0
0102 66.0 132.0
0103 66.0 132.0
>>> df_a.apply(lambda x: x/x-1+x.sum(),axis=1)
AAPL NIKE
0101 44.0 44.0
0102 66.0 66.0
0103 88.0 88.0
>>>
(위에서 x/x-1 이라는 표현은 0이라고 쓰면 aggregation()되어 버려서 그걸 막기위해 x로 0을 표현한것임)
포맷팅도 가능한것 같다.
.apply('{:03.0f}'.format)
transpose (row, col 교체.. 옆으로 눞히기)
>>> df_a = pd.DataFrame(a)
>>> df_a
AAPL NIKE
0101 11 33
0102 22 44
0103 33 55
>>> df_a.transpose()
0101 0102 0103
AAPL 11 22 33
NIKE 33 44 55
>>>
와 이것도 진짜 대박이다 @.@
sort
dict를 dataframe으로 바꿀때 기본적으로 row방향에 대해서 소트 해주는 것 같다.
• 흠.. 이거에 대해서 웹문서 찾아보는데 쉽지는 않네 ㅠ.ㅠ
*

column 방향으로는 소트해 주지 않는 것 같다.
tranpose()하면 다시 바뀐 row방향에 대해서(이전에 col방향이었던)또 소팅해서 보여주는 듯
여기 좋다.
여기서는 뭔가 심오한 토론들이
일부만 잘라서(보기, 로그찍기 등)
첫번째 컬럼으로 세 줄만 보고싶으면 다음처럼 하면 된다.
>>> a[a.columns[0]].head(3)
2015-06-10 [10250.0, 10300.0, 10283.3333333, 10266.666666...
2015-06-11 [10250.0, 10400.0, 10383.3333333, 10400.0, 103...
2015-06-12 [10183.3333333, 10150.0, 10100.0, 10066.666666...
Name: KR7000030007, dtype: object
>>>
컬럼을 2개이상 같이 보고 싶으면 좀 복잡한 것 같다. (seeborn으론 쉽다?)
element가 list일때 rolling이 안됨
>>> c
1 [1, 2, 3]
2 [2, 3, 4]
3 [3, 4, 5]
위처럼 element가 숫자가 아니라 list이면
>>> c.rolling(2).sum()
1 [1, 2, 3]
2 [2, 3, 4]
3 [3, 4, 5]
위처럼 rolling을 시도했을때 no action이 되어 버린다.
>>> c.apply(lambda x: pd.Series(x))
0 1 2
1 1 2 3
2 2 3 4
3 3 4 5
이때는 위처럼 Series로 바꿔주면 되더라
다시 list로 합치려면 다음처럼 하면 된다.
>>> d['haha']=c.values.tolist()
>>> d
0 1 2 haha
1 1 2 3 [1, 2, 3]
2 2 3 4 [2, 3, 4]
3 3 4 5 [3, 4, 5]
>>> e = d['haha']
>>> e
1 [1, 2, 3]
2 [2, 3, 4]
3 [3, 4, 5]
Name: haha, dtype: object
>>>
여기서 개인적인 생각으로는 d['haha'] = c.values.tolist() 이게 명확한 문법같지는 않다.
왜냐면 row 단위로 적용된다는건데.. 그 의미가 표면상에 나와있는 것 같지가 않기 때문
또한 위처럼 d, e 두 단계로 하지 않고 한단계로 할 수 있는 방법도 궁금한데.. 그냥 아래처럼 해봤을때는 컬럼별로 자동 분리가 되어 버렸다.
>>> f = pd.DataFrame(c.values.tolist())
>>> f
0 1 2
0 1 2 3
1 2 3 4
2 3 4 5
element 가 list일때 다루기
복잡해서 별도 세션으로 뺌 ㅠ.ㅠ
sum element column 추가
a = {'SWC': {'0101': [10, 20, 30], '0102': [40, 50, 60], '0103': [70,80,90]},
'AAPL': {'0101': [1, 2, 3], '0102': [4, 5, 6], '0103': [7,8,9]},
'NIKE': {'0101': [7, 8, 9], '0102': [10, 11, 12], '0103':[13,14,15]}}
df_a = DataFrame(a)
아래 처럼 꽤나 삽질해야했다.
코드가 깔끔하지도 않은 것 같고 ㅠ

z-score 계산
어려울거 같았는데 하다보니까 또 되네 ㅎㅎ

특정 row 지우기
쉬운 방법1. python slicing
그냥 파이선에서 쓰듯이 잘라쓰면된다.
예를 들어 아래처럼 하면 첫줄 지울 수 있듯이
>>> a = [11,22,33]
>>> a[1:]
[22, 33]
>>>
아래처럼 같은 방법으로 첫 줄을 지울 수 있다.
>>> a = {'AAPL': {'0101': [1, 2, 3], '0102': [4, 5, 6]}, 'NIKE': {'0101': [7, 8, 9], '0102': [10, 11, 12]}}
>>> a_df = DataFrame(a)
>>> a_df
AAPL NIKE
0101 [1, 2, 3] [7, 8, 9]
0102 [4, 5, 6] [10, 11, 12]
>>> a_df[1:]
AAPL NIKE
0102 [4, 5, 6] [10, 11, 12]
근데 in-place는 아니고 다른 변수에 대입해야 효과가 나는건데.. 다음처럼 drop과 index라는걸 쓰면 다른 변수에 대입하지 않고도 inplace로 지울 수 있다.
>>> a_df
AAPL NIKE
0101 [1, 2, 3] [7, 8, 9]
0102 [4, 5, 6] [10, 11, 12]
>>> a_df.drop(a_df.index[0:1], inplace=True)
>>> a_df
AAPL NIKE
0102 [4, 5, 6] [10, 11, 12]
iteration
컬럼 major order로 할때는 dict와 동일한 방식으로 처리 가능
row major order로 할때는 iterrows()를 사용하면 가능
integer index 관련
전체 개수는 len(df)로 파악 가능
각 줄은 df.iloc[x] 로 접근 가능
name index를 알 때 다음 방법으로 integer index에 접근 가능
df.index.get_loc('name_index')
Series
series[true-false series]하면 true인것만 모인 series가 되는 것 같다.
'Programming' 카테고리의 다른 글
| Python mixin 패턴 정리 (0) | 2026.05.17 |
|---|---|
| Matrix Completion과 추천 시스템 정리 (0) | 2026.05.17 |
| Python decorator class와 type 개념 정리 (0) | 2026.05.16 |
| 최소제곱법과 선형회귀 Cost Function 정리 (0) | 2026.05.16 |
| Python unittest 사용법: TestCase와 test_ 메서드 (0) | 2026.05.16 |
