[Pandas] DataFrame 제어
Json을 DataFrame으로 바꾸기#
import numpy as np
import pandas as pd
import json
with open('./data/student_json_column.json', 'r', encoding='utf8') as f:
data_dict = json.load(f)
# 잘 생각해보면 json 형식이 python의 dictionary와 유사함
print(data_dict)
df = pd.DataFrame.from_dict(data_dict, orient='columns')
OpenAPI를 활용하여 Data를 가져와서 DataFrame으로 만들어보자#
여기서는 OpenAPI를 사용하기 쉬운 영화진흥위원회 OpenAPI를 사용했다.
# 1. 필요한 module부터 import
import numpy as np
import pandas as pd
import json
import urllib
# 2. 영화진흥위원회 Open API를 호출하기 위한 url이 필요
movie_url = 'http://www.kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json'
query_string = '?key=3c551xxxxxxxxxxxxxxxxxxxxxxxxxxxx&targetDt=20210818' # ?key에는 회원가입 후 발급받은 key를 입력
last_url = movie_url + query_string
# 이 url을 이용하여 서버 프로그램을 호출해보자
result_obj = urllib.request.urlopen(last_url)
print(result_obj) # <http.client.HTTPResponse object at 0x00000187C0B1D7C8>
result_json = result_obj.read()
result_dict = json.loads(result_json) # json to dict
# display(result_dict)
result_df = pd.DataFrame.from_dict(result_dict)
# 우리가 원하는 DataFrame의 형태
# rank title saleAmt
# display(result_df, orient='columns')
rank_list = list()
title_list = list()
sale_Amt_list = list()
for tmp in result_dict["boxOfficeResult"]["dailyBoxOfficeList"]:
rank_list.append(tmp['rank'])
title_list.append(tmp['movieNm'])
sale_Amt_list.append(tmp['salesAmt'])
# display(rank_list)
# display(title_list)
# display(sale_Amt_list)
df = pd.DataFrame({'rank': rank_list, 'title': title_list, 'sale_Amt': sale_Amt_list})
DataFrame의 index와 column명을 제어해보자#
import numpy as np
import pandas as pd
data = {'이름': ['아이유', '김연아', '홍길동', '강감찬'], '학과': ['수학과', '기계과', '철학과', '경영학과'], '학년': [1, 4, 2, 3], '평점': [1.5, 3.9, 2.9, 4.2]}
df = pd.DataFrame(data, columns=['학과', '이름', '학년', '평점', '등급']) # key를 통해서 index 순서를 제어할 수 있다 만약 없다면 NaN값으로 생성됨
NaN
: Not a Number 일반적인 NaN의 의미 : 숫자로 간주하여 숫자 연산이 가능 ==> 10 + NaN = NaN (0) 허용 Pandas에서는 조금 다른 의미로 사용됨 ==> missing value(결치값)를 의미하는 값. NULL(값이 없음)은 일반적인 프로그래밍 값에서 사용됨 즉 레퍼런스한 값이 없음 python에선 none
DataFrame의 간단한 분석기능#
import numpy as np
import pandas as pd
data = {'이름': ['아이유', '김연아', '홍길동', '강감찬'], '학과': ['수학과', '기계과', '철학과', '경영학과'], '학년': [1, 4, 2, 3], '평점': [1.5, 3.9, 2.9, 4.2]}
df = pd.DataFrame(data, columns=['학과', '이름', '학년', '평점'])
display(df.describe())
DataFrame의 indexing, slicing, boolean indexing, fancy indexing#
import numpy as np
import pandas as pd
data = {'이름': ['아이유', '김연아', '홍길동', '강감찬', '이순신'], '학과': ['수학과', '기계과', '철학과', '경영학과', '철학과'], '학년': [1, 4, 2, 3, 2], '평점': [1.5, 3.9, 2.9, 4.2, 4.5]}
df = pd.DataFrame(data, columns=['학과', '이름', '학년', '평점'], index=['one', 'two', 'three', 'four', 'five'])
column을 제어하는 여러가지 방법(CRUD)
- DataFrame에서 특정 column을 추출할 수 있어요
print(df['이름']) # Series로 결과가 return되요
print(df.이름) # 이렇게도 사용이 가능하나, 잘 사용하지 않아요. 반복처리가 힘들기 때문
- DataFrame에서 특정 column의 value를 수정할 수 있어요
# 이렇게 추출한 Series는 DataFrame안의 데이터를 보여주는 view의 역할을 수행
s_name = df['이름'].copy() # copy()를 통해 새로운 Series를 생성할 수 있다.
s_name['one'] = '장범준'
- 2개 이상의 column을 추출하려면? Fancy indexing
# 결과는 당연히 DataFrame으로 return되요
display(df[['이름', '평점']])
- DataFrame에서 특정 column에 값을 수정하려면?
df = pd.DataFrame(data, columns=['학과', '이름', '학년', '평점', '등급'], index=['one', 'two', 'three', 'four', 'five'])
df['등급'] = 'A' # broadcasting 을 통해 데이터가 채워짐
df['등급'] = ['A', 'B', 'C', 'D', 'E']
df['등급'] = np.array(['A', 'B', 'C', 'D', 'E']) # list와 np.array 둘다 사용가능하지만, 저장되는건 np.array 형태로 사용됨
- DataFrame에서 특정 value들만 가져오고 싶을 때
print(df.values)
# [['수학과' '아이유' 1 1.5 nan]
# ['기계과' '김연아' 4 3.9 nan]
# ['철학과' '홍길동' 2 2.9 nan]
# ['경영학과' '강감찬' 3 4.2 nan]
# ['철학과' '이순신' 2 4.5 nan]]
# 특정 column들만 ndarray로 추출하고 싶을 때
print(df[['이름', '평점']].values)
print(df[['이름', '평점']].to_numpy)
- 새로운 column을 DataFrame에 추가하려면 어떻게 해야 하나요?
df['나이'] = [20, 25, 21, 24, 29] # 모든 record에 값을 다 채워줘야 해요
df['나이'] = [20, 25, 21, 24] # 이와 같이 shape? 를 맞추지 않으면 error
s = pd.Series([20, 25, 21, 24, 29], index=['one', 'two', 'three', 'four', 'five'])
# 여기서 추가가 안되는 이유는 미리 정한 index를 기반으로 정해지기 때문에 미리 index를 지정해준 경우 넣으려고 하는 Series역시 같은 index를 주어야 정상적으로 적용이 된다.
s = pd.Series([20, 24, 29], index=['one', 'four', 'five'])
# 이런 식으로 index만 맞다면, 원하는 곳에만 데이터를 집어 넣는 것도 가능하다
df['나이'] = s
- cloumn을 추가하거나 clomn 값을 수정할 때 연산을 통해서 할 수 있다
# naive한 방식
df['평점'] = [1.65, 4.18, .....]
# 연산 처리
df['평점'] = df['평점'] * 1.1
df['장학여부'] = df['평점'] > 3.0
- 원하는 column을 삭제하려면 어떻게?
new_df = df.drop('등급', axis=1, inplace=False)
new_df = df.drop('two', axis=0, inplace=False) # inplace 옵션에 따라 원본을 지울지(True) 사본을 만들어서 거기에 적용할지(False) 가 결정된다. 역시 False가 default이다
row(record, 행)를 제어하는 여러가지 방법 (CRUD)
import numpy as np
import pandas as pd
data = {'이름': ['아이유', '김연아', '홍길동', '강감찬', '이순신'], '학과': ['수학과', '기계과', '철학과', '경영학과', '철학과'], '학년': [1, 4, 2, 3, 2], '평점': [1.5, 3.9, 2.9, 4.2, 4.5]}
df = pd.DataFrame(data, columns=['학과', '이름', '학년', '평점', '등급'], index=['one', 'two', 'three', 'four', 'five'])
# column indexing에선?
# df['이름'] # 가능
# slicing을 하면 될까? == 안됨, boolean indexing 역시 안됨
# df['이름':'평점']
# display(df[['이름', '학년', '평점']]) # 가능
# row indexing(index 숫자를 이용해보아요)
# df[1] # Error index 숫자를 이용한 단일 indexing이 안됨
df[1:3] # ok index 숫자를 이용한 slicing은 가능함 == DataFrame형태의 view로 만들어짐
df[2:] # ok
# df[[1, 3]] # Error index 숫자를 이용한 fancy indexing이 안됨
# row indexing(지정된 index 이용)
# df['two'] # Error 지정 index를 이용한 단일 indexing이 안됨(단일 row 추출이 안됨)
df[1:2] # ok slicing은 가능
df['two': 'four'] # ok
# df['two': -1] # Error 숫자 index와 지정 index를 혼용해서 사용할 수 없음
#df[['one', 'three']]# Error
# 결과적으로는 slicing만 가능하다.
# row indexing은 이렇게 하는게 좋아오 loc[지정 index]
df.loc['two'] # ok loc와 지정 index를 이용하면 단일 row가 Series로 나오는데 주의할 점은 index가 column명으로 나온다
df.loc['two':'four'] # ok row slicing이 가능
df.loc['two':] # ok
# df.loc['two': -1] # Error 혼용에서 사용하는건 무조건 안된다고 보면 된다.
df.loc[['two', 'four']] # ok
# df.loc['two': 'four', '학과'] # ok series로 return되요
df.loc['two':'four', '학과':'학년'] # ok loc를 이용하면 column에 대해서도 slicing이 가능
df.loc['two':'four', ['이름', '평점']] # ok loc를 이용하면 column에 대해서 fancy indexing도 가능
# loc는 숫자 index를 사용하지 못하지만 iloc를 사용하면 반대로 숫자 index를 사용할 수 있다
df.iloc[1] # ok 단일 행 선택 가능
df.iloc[1:3] # ok slicing도 가능
df.iloc[1:4, :2] # ok
df.iloc[[1,3],[1,3]] # ok
## 예제 문제
# 평점이 4.0을 초과하는 학생의 이름과 평점을 dataframe으로 출력하자
display(df.loc[(df['평점'] > 4), ['이름', '평점']])
# 이름이 아이유인 사람을 찾아서 이름과 학과를 dataframe으로 출력하세요
display(df.loc[df['이름'] == '아이유', ['이름', '학과']])
# 평점이 3.0을 초과하는 사람을 찾아 등급을 'A'로 설정하세요
df.loc[(df['평점'] > 3.0), '등급'] = 'A'
display(df)
# 평점이 1.5 이상 3.0 미만인 사람을 찾아 학과 이름 평점을 출력하세요
display(df.loc[(df['평점'] >= 1.5) & (df['평점'] < 3.0), ['학과', '이름', '평점']])
DataFrame의 행 추가 & 삭제#
import numpy as np
import pandas as pd
data = {'이름': ['아이유', '김연아', '홍길동', '장범준', '이순신'],
'학과': ['철학과', '수학과', '물리학과', '경영학과', '컴퓨터'],
'평점': [1.5, 2.0, 3.4, 2.9, 4.9],
'학년': [1, 3, 2, 1, 4]}
df = pd.DataFrame(data,
columns=['학과', '이름', '학년', '평점', '등급'],
index=['one', 'two', 'three', 'four', 'five'])
# 행 추가
df.loc['six',:] = ['미술', '신사임당', 2, 3.4, 'A']
df.loc['six',['학과', '이름']] = ['미술', '신사임당']
df.loc['one','평점'] = 4.8
# 행 삭제
# new_df = df.drop('three':'four', axis=0, inplace=False) # slicing으로는 삭제가 안되요!
new_df = df.drop(['three','four'], axis=0, inplace=False) # fancy indexing으로는 삭제가 되요!