<jupyter_start><jupyter_text>1. Pandas - 구조화된 데이터의 처리를 지원하는 Python 라이브러리. Python계의 엑셀!  Pandas란? - 구조화된 데이터의 처리를 지원하는 Python 라이브러리- 고성능 Array 계산 라이브러리인 Numpy와 통합하여, 강력한 “스프레드시트” 처리 기능을 제공- Pandas는 Numpy의 wapper. 즉, Numpy의 데이터 타입을 그대로 불러와서 사용할 수 있다.- 인덱싱, 연산용 함수, 전처리 함수 등을 제공함<jupyter_code>from pandas import Series, DataFrame
import pandas as pd
import numpy as np<jupyter_output><empty_output><jupyter_text>위의 세 줄은 고정으로 import 시켜놓고 가자.  데이터 로딩<jupyter_code>data_url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data' # Data URL<jupyter_output><empty_output><jupyter_text>Boston housing 문제의 데이터는 다음과 같이 되어있다.<jupyter_code>df_data = pd.read_csv(data_url, sep='\s+', header = None) # csv 타입 데이터 로드, separate는 빈공간으로 지정하고, Column은 없음<jupyter_output><empty_output><jupyter_text>'\s+'의 경우, '\s'는 정규 표현식으로서 공백문자(space)를 의미한다. 따라서 '\s+'는 "빈 칸으로 나눠서 띄워져 있는 것들은 다 가져와라." 라는 뜻으로 이해하면 된다.그리고 header는 첫 줄에 column 이름들이 들어가 있는 지를 보는 paremeter이다. 없으면 None을 주면 된다.<jupyter_code>df_data.head() # 처음 다섯줄 출력<jupyter_output><empty_output><jupyter_text>head를 찍으면 처음 5칸을 보여준다. column들이 없었으니 column을 따로 설정해보자.<jupyter_code>df_data.columns = [
    'CRIM','ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO' ,'B', 'LSTAT', 'MEDV'] 
# Column Header 이름 지정
df_data.head()<jupyter_output><empty_output><jupyter_text>---  2. Series  Pandas의 구성  Pandas는 기본적으로 Series와 DataFrame이라고 하는 두 가지 Object로 구성되어 있다.Series라는 것은 간단하게 말해서 numpy에서의 하나의 vector  일반적인 pandas의 활용   Series - Column Vector를 표현하는 object ~~~pythonexample_obj = Series()~~~<jupyter_code>list_data = [1,2,3,4,5]
example_obj = Series(data = list_data) # data에는 dict도 가능
print(example_obj)<jupyter_output>0    1
1    2
2    3
3    4
4    5
dtype: int64<jupyter_text>- index & values(data)  Series에서는 index를 신경쓰지 않아도 되지만 나중에 DataFrame에서 index를 신경쓰면 되고 DataFrame에서는 index가 중복이 가능하다.<jupyter_code>list_name = ["a","b","c","d","e"]
list_data = [1,2,3,4,5]
example_obj = Series(data = list_data, index=list_name) # index 이름을 지정
print(example_obj)<jupyter_output>a    1
b    2
c    3
d    4
e    5
dtype: int64<jupyter_text>다음과 같이 직접적으로 접근도 가능하다.<jupyter_code>print(example_obj.index)
print(example_obj.values)
print(type(example_obj.values))<jupyter_output><class 'numpy.ndarray'><jupyter_text>다음과 같이 dictionary도 가능하다.<jupyter_code>dict_data = {"a":1, "b":2, "c":3, "d":4, "e":5}
example_obj = Series(dict_data, dtype=np.float32, name="example_data") 
# name - series 이름 설정. 쉽게 말해 하나의 column 이름 설정.
print(example_obj)<jupyter_output>a    1.0
b    2.0
c    3.0
d    4.0
e    5.0
Name: example_data, dtype: float32<jupyter_text>- Series의 인덱싱<jupyter_code>print(example_obj["a"]) # numpy와 다른점<jupyter_output>1.0<jupyter_text>다음과 같이 직접적으로 접근해서 값을 변경할 수도 있다.<jupyter_code>example_obj["a"] = 3.2
print(example_obj)<jupyter_output>a    3.2
b    2.0
c    3.0
d    4.0
e    5.0
Name: example_data, dtype: float32<jupyter_text>다음과 같은 것들도 가능하다.<jupyter_code>print(example_obj[example_obj > 2])
print(example_obj * 2)<jupyter_output>a     6.4
b     4.0
c     6.0
d     8.0
e    10.0
Name: example_data, dtype: float32<jupyter_text>- Data에 대한 정보 저장<jupyter_code>example_obj.name = "number" # series 이름 변경
example_obj.index.name = "alphabet" # index 이름 변경
print(example_obj)<jupyter_output>alphabet
a    3.2
b    2.0
c    3.0
d    4.0
e    5.0
Name: number, dtype: float32<jupyter_text>- index 값을 기준으로 series 생성<jupyter_code>dict_data_1 = {"a":1, "b":2, "c":3, "d":4, "e":5}
indexes = ["a","b","c","d","e","f","g","h"]
series_obj_1 = Series(dict_data_1, index=indexes)
print(series_obj_1)<jupyter_output>a    1.0
b    2.0
c    3.0
d    4.0
e    5.0
f    NaN
g    NaN
h    NaN
dtype: float64<jupyter_text>---  3. Dataframe - Series를 모아서 만든 Data Table = **기본 2차원**  DataFrame에서는 index와 columns 두 가지로 data를 찾을 수 있다.  ~~~pythonDataFrame()~~~<jupyter_code># Example from - https://chrisalbon.com/python/pandas_map_values_to_values.html
raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
            'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'],
            'age': [42, 52, 36, 24, 73],
            'city': ['San Francisco', 'Baltimore', 'Miami', 'Douglas', 'Boston']}
df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'city'])
print(df)<jupyter_output>first_name last_name  age           city
0      Jason    Miller   42  San Francisco
1      Molly  Jacobson   52      Baltimore
2       Tina       Ali   36          Miami
3       Jake    Milner   24        Douglas
4        Amy     Cooze   73         Boston<jupyter_text>대부분 csv파일을 이용해서 불러오기 때문에 위와 같이 dict 타입으로는 잘 쓰지 않는다. - column 선택 (numpy와 다른점)<jupyter_code>df = DataFrame(raw_data, columns = ["age", "city"])
print(df)<jupyter_output>age           city
0   42  San Francisco
1   52      Baltimore
2   36          Miami
3   24        Douglas
4   73         Boston<jupyter_text>- 새로운 column 추가<jupyter_code>df = DataFrame(raw_data, columns = ["first_name","last_name", "age", "city", "debt"])
print(df)<jupyter_output>first_name last_name  age           city debt
0      Jason    Miller   42  San Francisco  NaN
1      Molly  Jacobson   52      Baltimore  NaN
2       Tina       Ali   36          Miami  NaN
3       Jake    Milner   24        Douglas  NaN
4        Amy     Cooze   73         Boston  NaN<jupyter_text>- column 선택 - series 추출property를 아래와 같이 두 가지 방식으로 쓸 수 있다.<jupyter_code>print(df.first_name)
print(df["first_name"])<jupyter_output>0    Jason
1    Molly
2     Tina
3     Jake
4      Amy
Name: first_name, dtype: object<jupyter_text>- loc & iloc    - loc(index location) - index 이름    - iloc(index position) - index number    대체적으로 loc은 잘 안씀<jupyter_code>print(df.loc[2:]) # row values
print(df["first_name"].iloc[2:]) # column values<jupyter_output>2    Tina
3    Jake
4     Amy
Name: first_name, dtype: object<jupyter_text>df["first_name"]만 하게 되면 series가 튀어나온다.  다른 것으로 비교해보자.<jupyter_code># Example from - https://stackoverflow.com/questions/31593201/pandas-iloc-vs-ix-vs-loc-explanation
s = pd.Series(np.nan, index=[49,48,47,46,45, 1, 2, 3, 4, 5])
print(s.loc[:3]) # index의 이름이 3이라는 것까지 실행해라.<jupyter_output>49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN
dtype: float64<jupyter_text>loc은 위와 같이 index의 이름이 3인 것까지 출력하는 것을 볼 수 있다.<jupyter_code>print(s.iloc[:3])<jupyter_output>49   NaN
48   NaN
47   NaN
dtype: float64<jupyter_text>iloc은 위와 같이 0, 1, 2 즉, index의 number가 3번째 전까지 출력하는 것을 볼 수 있다. - column에 새로운 data 할당 (많이 사용하는 기법)<jupyter_code>df.debt = df.age > 40
print(df)<jupyter_output>first_name last_name  age           city   debt
0      Jason    Miller   42  San Francisco   True
1      Molly  Jacobson   52      Baltimore   True
2       Tina       Ali   36          Miami  False
3       Jake    Milner   24        Douglas  False
4        Amy     Cooze   73         Boston   True<jupyter_text>- transpose<jupyter_code>df = df.T
print(df)<jupyter_output>0          1      2        3       4
first_name          Jason      Molly   Tina     Jake     Amy
last_name          Miller   Jacobson    Ali   Milner   Cooze
age                    42         52     36       24      73
city        San Francisco  Baltimore  Miami  Douglas  Boston
debt                 True       True  False    False    True<jupyter_text>- DataFrame에 들어가있는 값 출력<jupyter_code>print(df.values)<jupyter_output>[['Jason' 'Molly' 'Tina' 'Jake' 'Amy']
 ['Miller' 'Jacobson' 'Ali' 'Milner' 'Cooze']
 [42 52 36 24 73]
 ['San Francisco' 'Baltimore' 'Miami' 'Douglas' 'Boston']
 [True True False False True]]<jupyter_text>- csv로 변환<jupyter_code>df_csv = df.to_csv()
print(df_csv)<jupyter_output>,0,1,2,3,4
first_name,Jason,Molly,Tina,Jake,Amy
last_name,Miller,Jacobson,Ali,Milner,Cooze
age,42,52,36,24,73
city,San Francisco,Baltimore,Miami,Douglas,Boston
debt,True,True,False,False,True<jupyter_text>- column 삭제<jupyter_code>df = df.T
print(df)
del df["debt"]
print(df)<jupyter_output>first_name last_name age           city
0      Jason    Miller  42  San Francisco
1      Molly  Jacobson  52      Baltimore
2       Tina       Ali  36          Miami
3       Jake    Milner  24        Douglas
4        Amy     Cooze  73         Boston<jupyter_text>---  4. Selection & Drop- 가져올 것들은 가져오고, 지울 것들은 지우고 하는 방법 소개<jupyter_code>!pip install xlrd
df = pd.read_excel("./excel_comp_data.xlsx")
print(df)<jupyter_output>account                              name  \
0    211829        <NAME> Hilpert   
1    320563                    Walter-Trantow   
2    648336        <NAME> and Price   
3    109996       <NAME> and Bode   
4    121213                     Bauch-Goldner   
5    132971  <NAME>   
6    145068                        Casper LLC   
7    205217                  Kovacek-Johnston   
8    209744                    Champlin-Morar   
9    212303                    Gerhold-Maggio   
10   214098       <NAME>   
11   231907                        Hahn-Moore   
12   242368      <NAME> Donnelly   
13   268755                       Walsh-Haley   
14   273274                     McDermott PLC   

                                  street               city          state  \
0                     34456 Sean Highway         New Jaycob          Texas   
1                      1311 Alvis Tunnel      Port Khadijah[...]<jupyter_text>Selection with column names - 1개의 column name 선택series 객체를 가져온다.<jupyter_code>df["account"].head(3)<jupyter_output><empty_output><jupyter_text>- 1개 이상의 column names 선택여러 개의 column name을 가져오고 싶을 때는 반드시 [ ]으로 묶어서 넣어주어야 한다. 그리고 위와 다르게 dataframe 객체를 가져온다.<jupyter_code>df[["account", "street", "state"]].head(3)<jupyter_output><empty_output><jupyter_text>Selection with index number - column 이름 없이 사용하는 index number는 row를 기준 가져옴<jupyter_code>df[:3]<jupyter_output><empty_output><jupyter_text>- column이름과 함께 row index 사용시, 해당 column만<jupyter_code>df["account"][:3]<jupyter_output><empty_output><jupyter_text>앞에 column을 지정해주었기 때문에 series data로 변했다.  Series selection<jupyter_code>account_serires = df["account"]
account_serires[:3]<jupyter_output><empty_output><jupyter_text>- 1개 이상의 index<jupyter_code>account_serires[[1,5,2]]<jupyter_output><empty_output><jupyter_text>- Boolean index<jupyter_code>account_serires[account_serires < 250000]<jupyter_output><empty_output><jupyter_text>Index 변경- 고유한 번호가 있는 경우, 따로 index를 두기 보다 그 값을 index로 해서 쓰고 싶은 경우도 있다. 그럴 경우 다음과 같이 하면 된다.- del을 해주는 것은 원래의 account가 남기 때문이다.<jupyter_code>df.index = df["account"]
del df["account"]
df.head()<jupyter_output><empty_output><jupyter_text>Basic, loc, iloc selection - Column과 index number (column의 숫자가 많을 경우 쓰는 방법)<jupyter_code>df[["name","street"]][:2]<jupyter_output><empty_output><jupyter_text>- Column과 index name (거의 안쓰는 방법)<jupyter_code>df.loc[[211829,320563],["name","street"]]<jupyter_output><empty_output><jupyter_text>- Column number와 index number (제일 많이 쓰는 방법)column이 몇 개 없을 떄는 iloc이 제일 편하다.<jupyter_code>df.iloc[:2,:2]<jupyter_output><empty_output><jupyter_text>개인적으로 iloc을 많이 쓸 것 같다. column에 대해서도 슬라이싱을 할 때 다음과 같이 쓰면 편하다.<jupyter_code>df["name"].iloc[2:]<jupyter_output><empty_output><jupyter_text>index 재설정<jupyter_code>df.index = list(range(0,15))
df.head()<jupyter_output><empty_output><jupyter_text>index 이름만 account로 돌려놓자. (values들은 사라짐..)<jupyter_code>df.index.name = "account"
df.head()<jupyter_output><empty_output><jupyter_text>Data drop - index number로 drop- column 단위로 없애줄 때는 del을 이용 ex) del df["debt"]- row 단위로 없앨 때 사용- drop을 쓰는 경우는 크게 없음- 잘 못 들어온 data를 날릴 때 주로 사용<jupyter_code>df.drop(1)<jupyter_output><empty_output><jupyter_text>- 한개 이상의 Index number로 drop<jupyter_code>df.drop([0,1,2,3])<jupyter_output><empty_output><jupyter_text>- axis 지정으로 축을 기준으로 drop -> column 중에 “city”그냥 del이 더 편하다.<jupyter_code>df.drop("city", axis=1)<jupyter_output><empty_output><jupyter_text>실제로 데이터가 사라지지는 않는다. pandas에서는 원본 데이터를 삭제를 쉽게 안한다. 원본 데이터를 변환해주고 싶으면 반드시 inplace=True를 추가로 적어주어야한다. inplace=False를 하면 copy본을 따로 만든다. 그리고 그 copy본에서 삭제를 한다. 이부분은 밑에 Replace function에서 다시 보자. 참고로 axis는 0이 항상 가장 큰 차원(축)이고, 숫자가 크면 클수록 작은 차원(축)이 된다. ---  5. Dataframe Operations  Series operation- index을 기준으로 연산수행- 겹치는 index가 없을 경우 NaN값으로 반환<jupyter_code>s1 = Series(range(1,6), index=list("abced"))
print(s1)
s2 = Series(range(5,11), index=list("bcedef"))
print(s2)
s1.add(s2)
print(s1 + s2)<jupyter_output>a     NaN
b     7.0
c     9.0
d    13.0
e    11.0
e    13.0
f     NaN
dtype: float64<jupyter_text>Dataframe operation- df는 column과 index를 모두 고려- add operation을 쓰면 NaN값 0으로 변환 - Operation types: add, sub, div, mul<jupyter_code>df1 = DataFrame(np.arange(9).reshape(3,3), columns=list("abc"))
print(df1)
df2 = DataFrame(np.arange(16).reshape(4,4), columns=list("abcd"))
print(df2)
print(df1 + df2)
df1.add(df2, fill_value=0)<jupyter_output><empty_output><jupyter_text>Series + Dataframe- column을 기준으로 broadcasting이 발생함<jupyter_code>df = DataFrame(np.arange(16).reshape(4,4), columns=list("abcd"))
print(df)
s = Series(np.arange(10,14), index=list("abcd"))
print(s)
print(df + s)<jupyter_output>a   b   c   d
0  10  12  14  16
1  14  16  18  20
2  18  20  22  24
3  22  24  26  28<jupyter_text>반대로 row를 기준으로 broadcasting을 할 수도 있다.<jupyter_code>s2 = Series(np.arange(10,14))
print(df)
print(s2)
df.add(s2, axis=0)<jupyter_output><empty_output><jupyter_text>---  6. Lambda, Map, Apply- 실제로 상당히 실용적인 내용!- Map이나 Replace를 써서 데이터를 변경해줄 때 많이 쓴다. - Map - series 추가, 데이터 변환 / Replace - 데이터 변환만- Apply는 element 단위가 아니라 series 단위로 min, max, mean, sum 등을 구할 때 많이 사용. 통계 데이터 구할 때 많이 사용- Applymap for dataframe은 전체적으로 element 단위에 적용할 때 사용  Lambda 함수- 한 줄로 함수를 표현하는 익명 함수 기법- Lisp 언어에서 시작된 기법으로 오늘날 현대언어에 많이 사용- **lambda** argument : expression<jupyter_code>f = lambda x,y: x + y
print(f(1,4))<jupyter_output>5<jupyter_text>이름을 할당하지 않고도 가능하다.<jupyter_code>(lambda x: x +1)(5)<jupyter_output><empty_output><jupyter_text>Map 함수- 함수와 sequence형 데이터를 인자로 받아- **각 element마다** 입력받은 함수를 적용하여 list로 반환 - 일반적으로 함수를 lambda형태로 표현함- **map** (function, sequence)<jupyter_code>ex = [1,2,3,4,5]
f = lambda x: x ** 2
print(list(map(f, ex)))<jupyter_output>[1, 4, 9, 16, 25]<jupyter_text>두 개이상의 argument가 있을 때는 두 개의 sequence형을 써야한다.<jupyter_code>f = lambda x, y: x + y
print(list(map(f, ex, ex)))<jupyter_output>[2, 4, 6, 8, 10]<jupyter_text>lambda와 같이 익명 함수 그대로 사용할 수도 있다.<jupyter_code>list(map(lambda x: x+x, ex))<jupyter_output><empty_output><jupyter_text>Map for series- Pandas의 series type의 데이터에도 map 함수 사용가능 - function 대신 dict, sequence형 자료등으로 대체 가능- 실제로 많이 사용- 데이터를 직접적으로 추가하거나 변환해줄 때 사용<jupyter_code>s1 = Series(np.arange(10))
s1.head(5)
s1.map(lambda x: x**2).head(5)<jupyter_output><empty_output><jupyter_text>- dict type으로 데이터 교체. 없는 값은 NaN10대, 20대, 30대를 구분한다거나 할 때 유용하게 **데이터를 변환** 해줄 수 있다.<jupyter_code>z = {1: 'A', 2: 'B', 3: 'C'}
s1.map(z).head(5)<jupyter_output><empty_output><jupyter_text>- 같은 위치의 데이터를 s2로 전환<jupyter_code>s2 = Series(np.arange(10,20))
s1.map(s2).head(5)<jupyter_output><empty_output><jupyter_text>Example - map for series- 제일 많이 쓰는 일반적인 테크닉<jupyter_code>df = pd.read_csv("wages.csv")
df.head()
df.sex.unique()<jupyter_output><empty_output><jupyter_text>map을 통해 sex_code이라는 **series를 추가** 해보자.<jupyter_code>df["sex_code"] = df.sex.map({"male":0, "female":1})
df.head()<jupyter_output><empty_output><jupyter_text>Replace function- Map함수의 기능 중 **데이터 변환 기능만** 담당- 데이터 변환 시 많이 사용하는 함수<jupyter_code>df.sex.replace({"male":0, "female":1}).head()
df.sex.replace(["male", "female"], # Target list
               [0,1],              # Conversion list
               inplace=True)       # 데이터 변환 결과를 적용
df.head()<jupyter_output><empty_output><jupyter_text>Apply for dataframe- map에서 했던 element 단위가 아닌, series **전체(column)에 해당** 함수를 적용- 입력값이 series 데이터로 입력받아 handling 가능- 통계 데이터를 뽑을 때 유용하게 사용<jupyter_code>df = pd.read_csv("wages.csv")
df.head()
df_info = df[["earn", "height","age"]]
df_info.head()
f = lambda x : x.max() - x.min()<jupyter_output><empty_output><jupyter_text>아래와 같이 각 column 별로 결과값 반환된다.<jupyter_code>df_info.apply(f)<jupyter_output><empty_output><jupyter_text>- 내장 연산 함수를 사용할 때도 똑같은 효과를 거둘 수 있음 - mean, std 등 사용가능<jupyter_code>df_info.sum()
df_info.apply(sum)<jupyter_output><empty_output><jupyter_text>- scalar 값 이외에 series값의 반환도 가능함<jupyter_code>f = lambda x : Series([x.min(), x.max(), x.mean()], 
                      index=["min", "max", "mean"])
df_info.apply(f)<jupyter_output><empty_output><jupyter_text>Applymap for dataframe- series 단위가 아닌 전체 dataframe의 **element 단위로** 함수를 적용함 - series 단위에 apply를 적용시킬 때와 같은 효과<jupyter_code>f = lambda x : -x
df_info.applymap(f).head(5)<jupyter_output><empty_output><jupyter_text>아래와 같이 apply를 이용하여 해당 series에 적용한 것과 동일한 효과를 볼 수 있다.<jupyter_code>df_info["earn"].apply(f).head(5)<jupyter_output><empty_output><jupyter_text>---  7. Pandas Built-in functions  describe- Numeric type 데이터의 요약 정보를 보여줌- 각 column들마다 통계자료들을 싹 다 뽑아준다. 제일 많이 쓰는 것 중에 하나!<jupyter_code>df = pd.read_csv("./wages.csv")
df.head()
df.describe()<jupyter_output><empty_output><jupyter_text>unique- series data의 유일한 값을 list를 반환함. 다시 말해 series data의 category set을 보여준다.- 유일한 인종의 값 list 출력category형 데이터가 몇 개가 있는 지 모를 때 많이 사용한다.<jupyter_code>df.race.unique()<jupyter_output><empty_output><jupyter_text>label str - > index 값으로 변환 - dict type으로 index<jupyter_code>np.array(dict(enumerate(sorted(df["race"].unique()))))<jupyter_output><empty_output><jupyter_text>- label index 값과 label 값 각각 추출<jupyter_code>value = list(map(int, np.array(list(enumerate(df["race"].unique())))[:, 0].tolist()))
key = np.array(list(enumerate(df["race"].unique())), dtype=str)[:, 1].tolist()<jupyter_output><empty_output><jupyter_text>int로 변환해주기 위해 map 사용. map 앞에는 무조건 list가 붙어야함.<jupyter_code>print(value)
print(key)<jupyter_output>[0, 1, 2, 3]
['white', 'other', 'hispanic', 'black']<jupyter_text>- label str - > index 값으로 변환<jupyter_code>df["race"].head()
df["race"].replace(to_replace=key, value=value, inplace=True)
df.head()<jupyter_output><empty_output><jupyter_text>위와 같이 labeling coding을 할 때 pandas를 이용해서 쉽게 변환해줄 수 있다. string값들을 다 int로 바꿔줘서 label을 만드는 것. 위에서 배웠던 replace로도 가능하다. - 성별에 대해서도 동일하게 적용<jupyter_code>value = list(map(int, np.array(list(enumerate(df["sex"].unique())))[:, 0].tolist()))
key = np.array(list(enumerate(df["sex"].unique())), dtype=str)[:, 1].tolist()
print(value)
print(key)<jupyter_output>[0, 1]
['male', 'female']<jupyter_text>- ”sex”와 “race” column의 index labelling<jupyter_code>df["sex"].head()
df["sex"].replace(to_replace=key, value=value, inplace=True)
df.head()<jupyter_output><empty_output><jupyter_text>sum- 기본적인 column 또는 row 값의 연산을 지원- sub, mean, min, max, count, median, mad, var 등<jupyter_code>df.sum(axis=0) # axis - 아래 화살표를 손으로 표시하고 오른쪽으로 이동
df.sum(axis=1).head() # axis - 오른쪽 화살표를 손으로 표시하고 아래로 이동<jupyter_output><empty_output><jupyter_text>isnull- column 또는 row 값의 NaN (null) 값의 index를 반환함<jupyter_code>df.isnull().head()<jupyter_output><empty_output><jupyter_text>- Null인 값의 합<jupyter_code>df.isnull().sum()<jupyter_output><empty_output><jupyter_text>sort_values- column 값을 기준으로 데이터를 sorting<jupyter_code>df.sort_values(["age", "earn"], ascending=False).head(10) # ascending=Ture 하면 오름차순<jupyter_output><empty_output><jupyter_text>--- 지금까지는 pandas로 데이터들을 불러오고 계산하는 것들을 했다면, 여기서부터는 feature라는 거대한 모판을 만들기 위해 데이터들을 나누고 붙이는 것들을 할 것이다. 일반적으로 우리가 생각하는 toy 데이터들은 굉장히 깨끗한 형태로 올라오지만 실제로 DB에 있는 데이터들을 가져오면 굉장히 지저분하기 때문에 전처리를 많이 해주어야한다. DB에 있는 데이터들이 너무 크면 전처리 시간이 너무 오래 걸리기 때문에 이러한 시간을 줄이기 위해서 Pandas라는 것을 통해 깨끗한 형태로 바꾸어 보고자 하는 것이다.  8. Groupby  Groupby- SQL groupby 명령어와 같음 - split -> apply -> combine- 과정을 거쳐 연산함- 일반적으로 통계치를 낼 때 많이 사용한다.<jupyter_code>ipl_data = {'Team': ['Riders', 'Riders', 'Devils', 'Devils', 'Kings',
                     'Kings', 'Kings', 'Kings', 'Riders', 'Royals', 'Royals', 'Riders'],
            'Rank': [1, 2, 2, 3, 3,4 ,1 ,1,2 , 4,1,2],
            'Year': [2014,2015,2014,2015,2014,2015,2016,2017,2016,2014,2015,2017],
            'Points':[876,789,863,673,741,812,756,788,694,701,804,690]}
df = pd.DataFrame(ipl_data)
print(df)<jupyter_output>Team  Rank  Year  Points
0   Riders     1  2014     876
1   Riders     2  2015     789
2   Devils     2  2014     863
3   Devils     3  2015     673
4    Kings     3  2014     741
5    Kings     4  2015     812
6    Kings     1  2016     756
7    Kings     1  2017     788
8   Riders     2  2016     694
9   Royals     4  2014     701
10  Royals     1  2015     804
11  Riders     2  2017     690<jupyter_text><jupyter_code>df.groupby("Team")["Points"].sum()<jupyter_output><empty_output><jupyter_text>- 한 개 이상의 column을 묶을 수도 있음<jupyter_code>df.groupby(["Team", "Year"])["Points"].sum()<jupyter_output><empty_output><jupyter_text>Hierarchical index- Groupby 명령의 결과물도 결국은 dataframe- 두 개의 column으로 groupby를 할 경우, index가 두개 생성- **index가 두개 이상 생성** 될 때 어떻게 다룰 것이냐<jupyter_code>h_index = df.groupby(["Team", "Year"])["Points"].sum()
print(h_index)
h_index.index
h_index["Devils":"Kings"]<jupyter_output><empty_output><jupyter_text>Hierarchical index – unstack()- Group으로 묶여진 데이터를 matrix 형태로 전환해줌 - Groupby를 쓰고 unstack()을 해서 matrix로 만들어 주는 것. 많이 쓰는 패턴 중 하나- DB에 있는 값들을 feature를 생성할 때 사용하는 패턴<jupyter_code>h_index.unstack() # row - Team, column - Year, values - Points<jupyter_output><empty_output><jupyter_text>Hierarchical index – swaplevel()- Index level을 변경할 수 있음<jupyter_code>h_index
h_index.swaplevel()
h_index.swaplevel().sort_index()<jupyter_output><empty_output><jupyter_text>Hierarchical index – operations- Index level을 기준으로 기본 연산 수행 가능- 별로 많이 쓰진 않음<jupyter_code>h_index
h_index.sum(level=0) # level=0이면 첫 번째 column을 기준
h_index.sum(level=1) # level=1이면 두 번째 column을 기준<jupyter_output><empty_output><jupyter_text>아래부터는 Groupby 중에서 이러한 것들이 있다 정도로만 알면 된다. 잘 쓰이진 않는다.  Groupby – gropued- Groupby에 의해 Split된 상태를 추출 가능함- Tuple 형태로 그룹의 key 값 Value값이 추출됨<jupyter_code>df
grouped = df.groupby("Team")
grouped
for name, group in grouped:
    print(name)
    print(group)<jupyter_output>Devils
     Team  Rank  Year  Points
2  Devils     2  2014     863
3  Devils     3  2015     673
Kings
    Team  Rank  Year  Points
4  Kings     3  2014     741
5  Kings     4  2015     812
6  Kings     1  2016     756
7  Kings     1  2017     788
Riders
      Team  Rank  Year  Points
0   Riders     1  2014     876
1   Riders     2  2015     789
8   Riders     2  2016     694
11  Riders     2  2017     690
Royals
      Team  Rank  Year  Points
9   Royals     4  2014     701
10  Royals     1  2015     804<jupyter_text>- 특정 key값을 가진 그룹의 정보만 추출 가능<jupyter_code>grouped.get_group("Devils")<jupyter_output><empty_output><jupyter_text>- 추출된 group 정보에는 세 가지 유형의 apply가 가능함 - Aggregation: 요약된 통계정보를 추출해 줌- Transformation: 해당 정보를 변환해줌- Filtration: 특정 정보를 제거 하여 보여주는 필터링 기능- 일반적으로 데이터 분석보다는 데이터에 대해서 통계치를 뽑을 때 가끔 사용한다.  Groupby – aggregation- 요약된 통계정보를 추출해 줌<jupyter_code>grouped.agg(sum)
grouped.agg(np.mean)<jupyter_output><empty_output><jupyter_text>- 특정 컬럼에 여러개의 function을 Apply 할 수도 있음<jupyter_code>grouped['Points'].agg([np.sum, np.mean, np.std])<jupyter_output><empty_output><jupyter_text>Groupby – transformation- 해당 정보를 변환해줌- Aggregation과 달리 key값 별로 요약된 정보가 아님 - 개별 데이터의 변환을 지원함 $z_i = \frac{x_i-\mu}{\sigma}$<jupyter_code>score = lambda x: (x - x.mean()) / x.std()
grouped.transform(score)<jupyter_output><empty_output><jupyter_text>Groupby – filter- 특정 정보를 제거 하여 보여주는 필터링 기능- 특정 조건으로 데이터를 검색할 때 사용- 그나마 검색할 때 많이 쓸일이 있다.<jupyter_code>df.groupby('Team')
for name, group in grouped:
    print(name)
    print(group)
df.groupby('Team').filter(lambda x: len(x) >= 3)<jupyter_output><empty_output><jupyter_text>filter안에는 boolean 조건이 존재해야하고, len(x)는 grouped된 dataframe 개수이다.<jupyter_code>df.groupby('Team').filter(lambda x: x["Points"].max() > 800)<jupyter_output><empty_output><jupyter_text>---  9. Case study  Data- 시간과 데이터 종류가 정리된 통화량 데이터- https://www.shanelynn.ie/wp-content/uploads/2015/06/phone_data.csv<jupyter_code>import dateutil<jupyter_output><empty_output><jupyter_text>dateutil 패키지의 parse 명령을 쓰면 자동으로 형식 문자열을 찾아 datetime.datetime 클래스 객체를 만들어 준다.<jupyter_code>df_phone = pd.read_csv("./phone_data.csv")
df_phone.head()
df_phone['date'] = df_phone['date'].apply(dateutil.parser.parse, dayfirst=True)
df_phone.head()
df_phone.groupby('month')['duration'].sum()
df_phone[df_phone['item'] == 'call'].groupby('month')['duration'].sum()
df_phone.item.unique()
df_phone.groupby(['month', 'item'])['duration'].sum()
df_phone.groupby(['month', 'item'])['date'].count().unstack()<jupyter_output><empty_output><jupyter_text>여기서부터는 잘 안쓰이는 것들이지만 한번 연습삼아 해보고 넘어가자.<jupyter_code>df_phone.groupby('month', as_index=False).agg(sum)<jupyter_output><empty_output><jupyter_text>index는 필요없으므로 다음과 같이 해보자.<jupyter_code>df_phone.groupby('month', as_index=False).agg({"duration": "sum"}) # duration column을 sum해준다.<jupyter_output><empty_output><jupyter_text>as_index=True로 하면 다음과 같이 된다.<jupyter_code>df_phone.groupby('month').agg({"duration": "sum"})<jupyter_output><empty_output><jupyter_text>쿼리문제 1- Find the sum of the durations for each group- Find the number of network type entries- Get the first date per group<jupyter_code>df_phone.groupby(['month', 'item']).agg({'duration': sum,     
                                     'network_type': "count",
                                     'date': 'first'})<jupyter_output><empty_output><jupyter_text>쿼리문제 2- Find the min, max, and sum of the duration column- Find the number of network type entries- Get the min, first, and number of nunique dates<jupyter_code>df_phone.groupby(['month', 'item']).agg({'duration': min,      
                                     'network_type': "count", 
                                 'date': [min, 'first', 'nunique']}) # nunique - nunber of unique
# nunique - 하나만 있는 것이 아니라 2개 이상인 것들의 갯수<jupyter_output><empty_output><jupyter_text>---  10. Pivot table & Crosstab - 기본적으로 groupby로 다 가능하다. 특수한 경우에만 Pivot table이나 Crosstab을 사용한다.- 앞에서 배웠던 unstack()과 동일하다.  Pivot Table- 우리가 Excel에서 보던 그 것!- Index 축은 groupby와 동일함- Column에 추가로 labelling 값을 추가하여,- Value에 numeric type 값을 aggregation 하는 형태<jupyter_code>df_phone = pd.read_csv("./phone_data.csv")
df_phone['date'] = df_phone['date'].apply(dateutil.parser.parse, dayfirst=True)
df_phone.head()
df_phone.pivot_table(["duration"],
                     index=[df_phone.month, df_phone.item], 
                     columns=df_phone.network, aggfunc="sum", fill_value=0)<jupyter_output><empty_output><jupyter_text>Crosstab- 두 column에 교차 빈도, 비율, 덧셈 등을 구할 때 사용 - Pivot table의 특수한 형태- User-Item Rating Matrix 등을 만들 때 사용가능함<jupyter_code>df_movie = pd.read_csv("./movie_rating.csv")
df_movie
pd.crosstab(index=df_movie.critic, columns=df_movie.title, values=df_movie.rating,
            aggfunc="first").fillna(0)
df_movie.pivot_table(["rating"], index=df_movie.critic, columns=df_movie.title,
                     aggfunc="sum", fill_value=0)
df_movie.groupby(["critic","title"]).agg({"rating":"first"}).unstack().fillna(0)<jupyter_output><empty_output><jupyter_text>---  11. Merge & Concat  Merge- SQL에서 많이 사용하는 Merge와 같은 기능 - 두 개의 데이터를 하나로 합침- 많이 사용<jupyter_code>raw_data = {'subject_id': ['1', '2', '3', '4', '5', '7', '8', '9', '10', '11'],
            'test_score': [51, 15, 15, 61, 16, 14, 15, 1, 61, 16]}
df_a = pd.DataFrame(raw_data, columns = ['subject_id', 'test_score'])
df_a
raw_data = {'subject_id': ['4', '5', '6', '7', '8'],
            'first_name': ['Billy', 'Brian', 'Bran', 'Bryce', 'Betty'], 
            'last_name': ['Bonder', 'Black', 'Balwner', 'Brice', 'Btisan']}
df_b = pd.DataFrame(raw_data, columns = ['subject_id', 'first_name', 'last_name'])
df_b<jupyter_output><empty_output><jupyter_text>- subject_id 기준으로 merge (inner join)<jupyter_code>pd.merge(df_a, df_b, on='subject_id')<jupyter_output><empty_output><jupyter_text>- 두 dataframe이 column이름이 다를 때 (inner join)<jupyter_code>pd.merge(df_a, df_b, left_on='subject_id', right_on='subject_id')<jupyter_output><empty_output><jupyter_text>Join method   Left join<jupyter_code>pd.merge(df_a, df_b, on='subject_id', how='left')<jupyter_output><empty_output><jupyter_text>Right join<jupyter_code>pd.merge(df_a, df_b, on='subject_id', how='right')<jupyter_output><empty_output><jupyter_text>Full(outer) join<jupyter_code>pd.merge(df_a, df_b, on='subject_id', how='outer')<jupyter_output><empty_output><jupyter_text>Inner join<jupyter_code>pd.merge(df_a, df_b, on='subject_id', how='inner')
pd.merge(df_a, df_b, on='subject_id')
pd.merge(df_a, df_b, left_on='subject_id', right_on='subject_id')<jupyter_output><empty_output><jupyter_text>다 같은 값이 나온다.  Index based join- index를 둘 다 살리고 싶을 때 사용- subject_id로 하는 것이 아니라 맨 왼쪽에 있는 index가지고 합칠 때 사용- 거의 잘 안씀<jupyter_code>pd.merge(df_a, df_b, left_index=True, right_index=True)<jupyter_output><empty_output><jupyter_text>Concat- 같은 형태의 데이터를 붙이는 연산작업<jupyter_code>raw_data = {'subject_id': ['1', '2', '3', '4', '5'],
            'first_name': ['Alex', 'Amy', 'Allen', 'Alice', 'Ayoung'], 
            'last_name': ['Anderson', 'Ackerman', 'Ali', 'Aoni', 'Atiches']}
df_a = pd.DataFrame(raw_data, columns = ['subject_id', 'first_name', 'last_name'])
df_a
raw_data = {'subject_id': ['4', '5', '6', '7', '8'],
            'first_name': ['Billy', 'Brian', 'Bran', 'Bryce', 'Betty'], 
            'last_name': ['Bonder', 'Black', 'Balwner', 'Brice', 'Btisan']}
df_b = pd.DataFrame(raw_data, columns = ['subject_id', 'first_name', 'last_name'])
df_b
df_new = pd.concat([df_a, df_b])
df_new.reset_index()
df_a.append(df_b)
df_new = pd.concat([df_a, df_b], axis=1)
df_new.reset_index()<jupyter_output><empty_output><jupyter_text>---  12. Persistence- 혼자 사용할 때 말고 여러명이서 작업할 경우 데이터를 넘겨주고 넘겨받을 때, 데이터를 파일 형태로 만들어주는 작업들- 세 가지 형태가 있음    - Database persistence    - XLS persistence    - Pickle persistence  DB persistence- Data loading시 db connection 기능을 제공함- https://www.dataquest.io/blog/python-pandas-databases/<jupyter_code>!pip install pymysql
import sqlite3 # pymysql <- 설치

# Database 연결 코드
conn = sqlite3.connect("./flights.db")
cur = conn.cursor()
cur.execute("select * from airlines limit 5;")
results = cur.fetchall()
results<jupyter_output><empty_output><jupyter_text>- db 연결 conn을 사용하여 dataframe 생성<jupyter_code>df_airplines = pd.read_sql_query("select * from airlines;", conn)
df_airplines.head()
df_routes = pd.read_sql_query("select * from routes;", conn)
df_routes.head()<jupyter_output><empty_output><jupyter_text>XLS persistence- Dataframe의 엑셀 추출 코드- Xls 엔진으로 openpyxls 또는 XlsxWrite 사용- https://xlsxwriter.readthedocs.io/working_with_pandas.html<jupyter_code>!pip install openpyxl
!pip install XlsxWriter
writer = pd.ExcelWriter('./df_routes.xlsx', engine='xlsxwriter')
df_routes.to_excel(writer, sheet_name='Sheet1')
writer.save()<jupyter_output><empty_output><jupyter_text>Pickle persistence- 가장 일반적인 python 파일 persistence - to_pickle, read_pickle 함수 사용- 제일 편한 방법. 제일 많이 쓰는 방법<jupyter_code>df_routes.to_pickle("./df_routes.pickle")
df_routes_pickle = pd.read_pickle("./df_routes.pickle")
df_routes_pickle.head()
df_routes_pickle.describe() # Numeric type 데이터의 요약 정보를 보여줌<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13187385978629138, 'fraction_numerical': 0.03397836851707063, 'mean_word_length': 4.128989139515455, 'pattern_counts': {'":': 18, '<': 431, '<?xml version=': 0, '>': 438, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '113', 'dir': 'jupyter-structured-clean-dedup', 'id': '26336', 'license': 'MIT', 'n_tokens_mistral': 15350, 'n_tokens_neox': 16576, 'n_words': 2940, 'path': 'src/lectures/5_pandas.ipynb', 'repo_name': 'dongminleeai/Python-for-ML', 'size': '30790'}