[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)

  1. DataFrame에서 특정 column을 추출할 수 있어요
print(df['이름'])   #   Series로 결과가 return되요
print(df.이름)      #   이렇게도 사용이 가능하나, 잘 사용하지 않아요. 반복처리가 힘들기 때문
  1. DataFrame에서 특정 column의 value를 수정할 수 있어요
# 이렇게 추출한 Series는 DataFrame안의 데이터를 보여주는 view의 역할을 수행
s_name = df['이름'].copy()  #   copy()를 통해 새로운 Series를 생성할 수 있다.

s_name['one'] = '장범준'    
  1. 2개 이상의 column을 추출하려면? Fancy indexing
# 결과는 당연히 DataFrame으로 return되요

display(df[['이름', '평점']])
  1. 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 형태로 사용됨
  1. 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)
  1. 새로운 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
  1. cloumn을 추가하거나 clomn 값을 수정할 때 연산을 통해서 할 수 있다
#   naive한 방식
df['평점'] = [1.65, 4.18, .....]

#   연산 처리
df['평점'] = df['평점'] * 1.1

df['장학여부'] = df['평점'] > 3.0
  1. 원하는 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으로는 삭제가 되요!