Python/Pandas&Numpy

Pandas

metamong 2022. 3. 21.

1. 개념

* python library로 구조화된 데이터를 효과적으로 처리하고 저장한다

- array 계산에 특화된 NumPy를 기반으로 설계한다

- 2차원 데이터 외 대용량 데이터 처리에 효과적임

- 데이터 가공, 변환, 정제, 수집, 전처리, 통계, 시각화 작업 모두 가능

 

-1- 데이터 수집) 텍스트, CSV, Excel, HTML(BeautifulSoup), XML, Hdfs, db, JSON 여러 형태를 pandas는 읽을 수 있다

-2- 데이터 전처리) 전처리 과정을 반드시 거쳐야 함 - 분석가능한 형태로 변형 (null 처리, 이상치, 정규화 등등)

-3- 데이터 분석) 통계 & 시각화(matplotlib, seaborn)

-4- 예측) pandas에서는 지원하지 않음 - sklearn, Tensorflow, keras, pytorch, statesmodels, scypython 추가 library 이용

 

** 1차원 데이터 분석 → Series

** 2차원 데이터 분석 → Dataframe

2. Series

[1] 개념

* Series = NumPy의 array가 보강된 형태 (일종 특수한 dictionary)

- data와 index를 가지고 있다.

- numpy의 array에 index가 추가

- Series만을 단독으로 생성하여 사용할 일은 없고, 행 단위/ 컬럼 단위로 접근 시 Series 자료구조 개념으로 접근한다

 

data + index

 

* pd.Series() 

 

class pandas.Series(data=None, index=None, dtype=None, name=None, copy=False, fastpath=False)

 

- tuple, list, dictionary 모두 Series()를 통해서 Series를 만들 수 있다.

(* 이 때, tuple과 list는 index가 0부터 자동으로 생성(zero-based index)이 되고, dictionary의 경우 key가 index로 생성된다)

 

 index: 아니면 index 자체를 만들어서 Series 생성이 가능하다 (* pd.Series(data, index=[~]) (같은 값 index 생성 가능)

- index를 통한 접근은 [index]를 통해 Series의 내용을 불러올 수 있다 (그러나 list, tuple과 다르게 음수로는 접근 불가)

 dtype: Series 내의 dtype 데이터 타입 적용이 가능하다! (* pd.Series(data, dtype=np.~))

(* Series 자체가 numpy의 array가 보강된 형태여서 dtype에 np type을 붙여준다)

 name: Series의 이름을 붙일 수 있다. (* pd.Series(data, name='~'))

 

* Series 속성

- ndim (1차원이므로 무조건 1!)

- shape ( (n,)이 나옴!)

- name

- size (행 개수 * 열 개수의 값이 나옴 - 즉 n 나옴)

- dtype

- index

- sort_index (ascending True, False 옵션)

- values (type은 numpy.ndarray 나옴! - 앞에서 말했듯이 numpy의 ndarray를 보강한 형태이므로 :0)

- sort_values (ascending True, False 옵션)

- max

- min

- idxmax (가장 큰 값의 index가 반환된다)

- quantile (원하는 분위의 수가 반환됨)

- std (표준편차)

- unique

- value_counts()

 

 **Series.str.contains()

- Series, 또는 주어진 datframe의 한 column에서 원하는 문자가 들어있는 지를 찾아주는 method!

(여기서 중요한 건 str.contains()는 Series형태, 즉 한 column에만 적용할 수 있다는 점이다!)

- contains() 내부에 정규식 표현(regular expression)을 집어넣어 원하는 data 찾을 수 있음!

 

sr = pd.Series(['2','ewe','32wqa'])

sr.str.contains('e')

 

(실행결과) - False, True bool값을 리턴해준다

더보기
0    False
1     True
2    False
dtype: bool

 

→ ** Series.str.replace('a','b') = replace 'a' with 'b'

- Series, 또는 주어진 datframe의 한 column에서 기존 data의 글자(또는 문자열 전체)를 원하는 data로 바꿀 때 사용한다

 

 

- source) https://pandas.pydata.org/docs/reference/api/pandas.Series.html 

[2] Series의 values

* series는 값(values)을 ndarray 형태로 가지고 있다.

* 즉, data자체는 series인데 그 안에 들어가는 value는 ndarray인 배열 형태 - numpy (꼭꼭 기억!)

 

import pandas as pd

data = pd.Series([1,2,3,4])
print (data)
# 0 1
# 1 2
# 2 3
# 3 4
# dtype: int64

print(type(data))
#<class 'pandas.core.series.Series'>

print(data.values)
# [1 2 3 4]

print(type(data.values))
#<class 'numpy.ndarray'>

 

[3] indexing(+dtype)

* dtype인자로 데이터 타입을 지정할 수 있다. (위에서 언급함!)

 

data = pd.Series([1,2,3,4],dtype="float")
print(data.dtype) #float64

 

* index를 지정할 수 있고 index로 접근(indexing)이 가능하다

- 또한 index로 접근하여 원하는 value로 바꿀 수 있다.

- index를 숫자로 지정하면 헷갈림을 방지하기 위하여 zero-based index는 적용되지 않는다 (iloc은 zero-based로 고정됨!)

 

data = pd.Series([1,2,3,4],index=['a','b','c','d'])
data['c'] = 5

data[0] #'a'
data[-1] #'d'

 

[4] dictionary 활용

* Series는 dictionary와 매우 유사

- dictionary를 활용하여 series를 생성할 수 있다. (위에서 잠깐 언급함!)

 

population_dict = {
	'china': 141500,
	'japan': 12718,
	'korea': 5180,
	'usa': 32676
}

population = pd.Series(population_dict)

 

- 아래와 같이 만들어진 딕셔너리를 series로 변환이 가능하다

(index를 key인 나라이름, values는 딕셔너리 value 값인 인구수)

 

 

3. dataframe

* 데이터프레임 = 여러 개의 Series가 모여서 행과 열을 이룬 data

[1] dataframe 생성

{1} Series 합치기 & Series로 변경

- 예시) 앞의 population Series에 이어서 gdp Series를 만들고 dataframe으로 두 series를 합치면

 

gdp_dict = {
# ~
}

gdp = pd.Series(gdp_dict)

country = pd.DataFrame({
	'gdp': gdp,
	'population': population
 })

 

dataframe

 

- 2차원을 1차원 Series로 변경한다 (변경된 Series는 한 column만 value로 두고, 나머지는 multi-index로 변함)

 

df.unstack()

 

{2} Dictionary 활용하여 생성하기

data = {
'country' : ['china', 'japan', 'korea', 'usa'],
'gdp' : ~ ,
'population': ~ ,
}

country = pd.DataFrame(data)
country = country.set_index('country')

 

- set_index method를 통해 country column이 index로 들어간 것을 확인할 수 있다.

 

{3} 딕셔너리 & Series & Dataframe 비교

* dictionary는 data = {key:value} 형태로 표현된다.

* series는 배열로 Series() 또는 딕셔너리를 통해 만들 수 있다.

* dataframe은 series의 모음으로 만들 수 있으며 series data + index의 형태이다. 또는 dictionary에서 바로 dataframe으로도 만들 수 있다.

 

[2] dataframe 속성

- 아래에 다룰 해당 dataframe은 위에서 언급된 dataframe.

 

* shape & size & ndim & values & T

- shape은 dataframe의 data shape을 뜻함

(즉 해당 데이터프레임인 경우 (4,4)가 아니라 (4,2))

- T는 transpose의 T로 해당 dataframe의 행과 열을 서로 바꿔 뒤집음

 

print(country.shape) # (4,2)
print(country.size) # 8
print(country.ndim) # 2
print(country.values) # [[1409250000 ~

 

* nlargest - 특정 column을 기준(2개 이상도 가능)으로 가장 큰 n개의 data만 가져오겠다는 뜻! (꽤 많이 쓰인다! 꼭 기억하자....!!)

- 출력결과는 가장 큰 n개의 data가 포함된 '행' 전체로 구성된 dataframe이 return됨!

- keep인자) default first이며, 동일한 값인 경우 all이면 전체를 보여주고, last이면 index 기준 마지막 index data먼저 보여줌

- (중요) 정렬은 보장 x. 단순히 상위 n개만 가져옴

 

* nsmallest - nlargest와 반대개념! 내용은 똑같음 

 

df.nlargest(n,'column_name')

df.nsmallest(n,'column_name')

 

[3] dataframe - index & column에 이름 지정하기

* index & columns에 name 메소드 사용

 

country.index.name = "Country" #naming index
country.columns.name = "Info" #naming columns

print(country.index)
# Index(['china', 'japan', 'korea', 'usa'], dtype='object', name = 'Country')
print(country.columns)
# Index(['gdp','population'], dtype='object', name = 'Info')

 

[4] dataframe - save & load

* 엑셀이나 csv(comma separated value) 파일로 불러올 수 있고 저장도 가능하다

 

country.to_csv("./country.csv")
country.to_excel("country.xlsx")

country = pd.read_csv("./country.csv")
country = pd.read_excel("country.xlsx")

 

→ Excel) data 전체가 아니라 일부 data만 가져오고 싶을 때 header & usecols 사용 가능

 

ex) header는 1로 맨 위의 행만 제외하고 아래 행부터 가져올 때 아래와 같이 사용할 수 있다!

 

df = pd.read_excel("~.xlsx", header = 1)

 

ex) parse_cols 값에 가져오고 싶은 컬럼명만 지정해서 가져올 수 있다!

 

df = pd.read_excel("~.xlsx', usecols="column_names(list)")

 

[4.5] dataframe - pivot table

* 원하는 정보 일부만 모아서 데이터 확인용으로 pivot table을 만들 수 있다!

→ create a spreadsheet-style pivot table as a DataFrame

 

https://pandas.pydata.org/docs/reference/api/pandas.pivot_table.html 

 

ex)

 

df.pivot_table(index='a', values='b', aggfunc='mean')

 

- index와 values(내용물)을 기존 dataframe에 원하는 정보를 선택할 수 있으며, 이 때 집계함수를 기준으로 데이터를 나눠 보여줄 수 있다.

 

[5] 데이터 선택 및 변경

{1} 데이터 선택 - indexing / slicing

* .loc: 명시적인 index를 참조하는 인덱싱/슬라이싱 (index 값을 알고 있을 때 사용)

 

country.loc['china'] #indexing
country.loc['japan':'korea', :'population'] #slicing

 

- indexing) 위의 예의 경우 명시적으로 'china'라는 index에 해당하는 data를 불러오게 하였다.

 

 

- slicing) :을 사용하여 원하는 index 범위에 해당하는 data 불러오게 하였다.

(,기준 앞이 index, 뒤가 column)

 

 

♣ loc documentation ♣

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html

 

* .iloc: 암묵적으로, python 스타일의 정수 index indexing & slicing

- 상대적인 data간의 위치를 이용해서 값을 찾아낼 때 사용한다

 

country.iloc[0] #indexing
country.iloc[1:3,:2] #slicing

 

 

{2} 데이터 선택 - column 선택

* 컬럼명을 활용하여 dataframe에서 data 선택이 가능하다

 

country
country['gdp']
country[['gdp']]

 

 

- 대괄호를 한 번 씌우면 구성성분인 series 형태로 나오지만, 대괄호를 두 번 씌우면 dataframe 형태로 나온다.

 

{3} 데이터 선택 - 조건 활용

* masking 연산이나 query 함수를 활용하여 조건에 맞는 dataframe 행을 추출할 수 있다.

- dataframe 형태로 출력

 

country[country['population'] < 10000] #masking operation
country.query("population > 100000") #query function

 

 

- 조건 사용 시 and 연산자 (&)와 or 연산자 (|)를 사용해서 다양한 조건을 만들 수 있다. (마스킹 연산의 경우)

- query의 경우 영어로 and, or

 

(+) format string을 이용한 query 연산

- query() 안에 f를 치고 {}안에 format 형태를 넣으면 해당 format에 들어간 식이 query에서 돌아가 원하는 dataframe 형태를 반환!

 

ex) sepal_length값이 6.0이상인 경우의 dataframe 반환

 

n = 6.0
df_iris.query(f'sepal_length>{n}')

 

반환 결과

 

{4} 데이터 변경 - column 추가

* Series도 numpy array처럼 연산자 활용이 가능하다

- 연산자 활용을 통해 series가 생성되고 이를 집어넣고자 하는 데이터프레임의 한 column으로 추가가 가능하다

 

gdp_per_capita = country['gdp']/country['population']
country['gdp_per_capita'] = gdp_per_capita

 

 

{5} 데이터 변경 - 데이터 추가/수정

* list로 추가 or dictionary로 추가

* index 활용하여 데이터 수정

 

df = pd.DataFrame(columns = ['이름', '나이', '주소']) #making a dataframe
df.loc[0] = ['길동', '26', '서울'] #add data using a list
df.loc[1] = {'이름':'철수', '나이':'25', '주소':'인천'} #add data using a dictionary
df.loc[1, '이름'] = '영희' #명시적 indexing으로 revising data

 

 

{6} 데이터 변경 - NaN column 추가 & 삭제

* NaN값으로 초기화한 새로운 column 추가

- NaN을 추가하고 싶다면 import numpy as np → np.nan 추가

 

df['전화번호'] = np.nan #add new column and reset
df.loc[0,'전화번호'] = '01012341234' #using index - revise data

 

위의 예시

 

* NaN을 포함한 column을 삭제하려면 dropna() 함수를 사용하면 된다

- axis=0이면 NaN을 포함한 row(행)가 삭제되며, axis=1이면 NaN을 포함한 column(칼럼)이 삭제된다

(axis=0이면 행 방향, axis=1이면 열 방향임을 꼭 기억!)

- 특정 NaN만 삭제하고 싶으면 subset=['column_name']을 사용하여 해당 column에의 NaN을 포함한 행, 또는 열이 삭제된다

 

 dropna

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html 

 

(+) isnull.sum() & fillna()를 통해 NaN의 개수를 구하거나 NaN을 원하는 값으로 대체할 수 있다! (아래 집계함수 part에 설명)

{7} 데이터 변경 - column 삭제

* 데이터프레임에서 column 삭제 후 원본 변경

- axis) 1은 열 방향, 0은 행 방향

(여기서는 전화번호 컬럼을 지우기에 열, 즉 위에서 아래 방향으로 지우므로 1임)

- inplace) True는 원본을 변경, False는 원본을 변경하지 않는다는 뜻

(실무적으로 굉장히 중요한 파라미터로, 원본을 지키는 상태에서 변경할 수도 있기에 inplace 값 언제나 고려할 것)

 

df.drop('전화번호', axis=1, inplace = True) #delete a column

 

 

- columns = [~], index = [~]로 원하는 data 삭제가 가능하다

 

[6] 데이터프레임 정렬

{1} index 값 기준 정렬

* index 방향 & 오름차순, 내림차순

- axis = 0:  index 기준 정렬 (default 오름차순)

 

df = df.sort_index(axis=0)

 

 

- axis = 1:  index 기준 내림차순 정렬 (ascending = False)

 

df.sort_index(axis=1,ascending=False)

 

 

{2} column 값 기준 정렬

* default = 오름차순

 

df.sort_values('col1',ascending=True)

 

 

- ascending=False하면 그 반대로 정렬

 

* 여러 column 동시에 정렬도 가능

- (하단) col2 column 기준 오름차순 정렬 후, col1 column 기준 내림차순 정렬

(먼저 정렬된 data 내에서 다시 오름 or 내림차순으로 정렬한다)

 

df.sort_values(['col2','col1'],ascending=[True,False])

 

 

[7] 데이터프레임 분석용 함수

{1} count

* count 메서드를 활용하여 데이터 개수 확인이 가능하다 (default: NaN값 제외)

 

data = {
'korean' : [50,60,70],
'math' : [10, np.nan, 40]
}

df = pd.DataFrame(data, index = ['a','b','c'])
df.count(axis=0) #열 기준 count
df.count(axis=1) #행 기준 count

 

data type은 모두 int형

 

{2} max, min

* max, min 메서드를 활용하여 최대, 최소값 확인 가능

* default: 열 기준, NaN값 제외

 

data = {
'korean' : [50,60,70],
'math' : [10, np.nan, 40]
}

df = pd.DataFrame(data, index = ['a','b','c'])
df.max()
df.min()

 

float형

 

{3} sum, mean

* sum, mean 메서드를 활용하여 합계 및 평균 계산

* default: 열 기준, NaN값 제외

 

data = {
'korean' : [50,60,70],
'math' : [10, np.nan, 40]
}

df = pd.DataFrame(data, index = ['a','b','c'])
df.sum()
df.mean()

 

float형

 

* axis, skipna 인자를 활용하여 합계 및 평균을 계산할 수 있다. (행 기준, NaN값 포함할 경우)

- default로 원래 NaN을 포함하기에 False로 두어 NaN을 포함해 결과를 NaN을 출력한다.

 

data = {
'korean' : [50,60,70],
'math' : [10, np.nan, 40]
}

df = pd.DataFrame(data, index = ['a','b','c'])
df.sum(axis=1)
df.mean(axis=1, skipna=False)

 

 

* (NaN 해결) NaN값이 존재하는 column의 평균을 구하여 NaN값을 대체할 수 있다.

- fillna() 사용: 해당값을 NaN 대신 대체하겠다는 의미

 

B_avg = df['math'].mean()
print(B_avg) # 25.0

#replace NaN
df['math']=df['math'].fillna(B_avg)

#mean
df.mean(axis=1,skipna=False)

 

 

[8] 데이터프레임 - 그룹으로 묶기

{1} groupby

* 간단한 집계를 넘어서서 조건부로 집계하고 싶을 때 사용

 

df = pd.DataFrame({
'data1': range(6),
'data2': [4,4,6,0,6,1],
'key': ['A', 'B', 'C', 'A', 'B', 'C']
})

df.groupby('key').sum() #1번
df.groupby(['key','data1']).sum() #2번

 

 

- 첫번째 groupby는 각 key를 그룹으로 하여 각 그룹의 합을 나타내었고

- 두번째 groupby는 key로 그룹으로 묶은 다음 각 그룹에서 data1에 대해서도 그룹을 묶어 만들어진 세부그룹별 합계도 나타낼 수 있다.

 

{2} + aggregate

* groupby를 통해서 집계를 한 번에 계산할 수 있다.

- aggregate 메서드 활용 (여러 aggregate 함수를 각각 다른 column에 적용하여 원하는 대로 결과 표현 가능!)

 

df.groupby('key').aggregate(['min', np.median, max]) #1번
df.groupby('key').aggregate({'data1':'min', 'data2':np.sum}) #2번

 

 

- 첫번째 groupby의 경우 각 key를 그룹으로 하여 min, median, max 결과를 나타냄

- 두번째 groupby의 경우 집계할 데이터마다 집계방법을 달리 한 결과를 나타낸 것이다.

 

{3} filter

* groupby를 통해서 그룹 속성을 기준으로 데이터를 filtering할 수 있다.

- filter() 사용

 

def filter_by_mean(x):
	return x['data2'].mean() > 3

df.groupby('key').mean() #1번
df.groupby('key').filter(filter_by_mean) #2번

 

 

- 두번째 groupby의 경우 동일 key의 평균이 3보다 큰 경우에 해당하는 원본 데이터만 나타낸다는 뜻이다.

 

{4} apply, lambda

* groupby를 통해서 묶인 데이터에 함수를 적용한다.

 

df.groupby('key').apply(lambda x: x.max() - x.min())

 

 

- 모든 data에 apply 함수로 계산한 값을 나타내 준다.

 

→이와 같이 apply(function_name)을 적용하면 function_name 함수가 특정 column에 모두 적용된다! (매우 편리하고 많이 쓰이는 apply! 꼭 기억 :))

 

{5} get_group

* groupby로 묶인 data에서 key값으로 데이터를 가져올 수 있다.

- get_group("key") 사용

 

df = pd.read_csv("./univ.csv")

df.head()

df.groupby("시도").get_group("충남")
len(df.groupby("시도").get_group("충남")) # 94

 

 


 

 

 

 

* 출처) 2021 NIPA/AI 기본/응용 교육과정

댓글