오늘부터 일주일은 처음 파이썬 기초를 가르쳐주셨던 강사님이 다시 오셨습니다.
반가웠습니다. 짧은 시간도 인연인지 강의도 익숙하고, 잘 들을 수 있었습니다.
내용은 쉽지 않았습니다. pandas를 향후에 어느정도 쓸지 모르겠지만 교안 내용을 봤을때
하루에 정리하기는 좀 터무니 없는 양이다 라는 생각을 했습니다.

그래도 강의를 듣다보니 이런 부분은 함수를 하나씩 설명하기 보다는 찾아가면서
문제해결 하는게 맞다는 생각이 들었습니다. 양이 많기도 하고, 다양한 예제를
모두 정리할 수는 없는 일이니까요.
주요한 함수와 주의사항을 알려주고, 직접 사용해보도록 하는게 맞다는 생각을 했습니다.
우리 모두 클래스 배웠으니까요..;;


pandas 개요

import pandas as pd

test = pd.read_csv("/content/sample_data/california_housing_test.csv")
print(test.head())
print('')
print(test.tail())
print('')
# DataFrame 의 요약 정보 출력
print(test.info())

print(test.describe())

간단하게 샘플 csv 파일 불러오고, test 객체에할당합니다.
head() 함수는 파일의 상단에 5개 행을 출력합니다.
tail() 함수는 파일의 말단에 5개 행을 출력합니다.

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3000 entries, 0 to 2999
Data columns (total 9 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   longitude           3000 non-null   float64
 1   latitude            3000 non-null   float64
 2   housing_median_age  3000 non-null   float64
 3   total_rooms         3000 non-null   float64
 4   total_bedrooms      3000 non-null   float64
 5   population          3000 non-null   float64
 6   households          3000 non-null   float64
 7   median_income       3000 non-null   float64
 8   median_house_value  3000 non-null   float64
dtypes: float64(9)
memory usage: 211.1 KB
None

위의 정보는 info() 함수의 출력 결과입니다.
info() 함수는 이 객체(csv 파일)의 대략적인 정보를 출력합니다.
클래스 정보, 데이터 양과 컬럼수와 컬럼의 이름, Non-Null의 수, 각 컬럼의 데이터타입 등
정보를 출력합니다.
Null 이 있으면 머신러닝 모델의 대부분을 돌리지 못합니다. (딥러닝도 거의 동일)
xgboost / 딥러닝 일부 모델 중 옵션변경 할 수 있습니다. (Null -> 하나의 값! 으로 치환해서 쓰는 경우 있음)
Null -> 0(X), NAN(X) Null을 임의로 치환하는 것은 굉장히 위험합니다.
Null 최대한 제거하고 분석하는 것을 추천 -> Null을 가지고 있는 데이터가 다른 데이터와 다른지 파악하는 것이 중요합니다.
이런 것보다 더 중요하다고 말씀하신건 요구정의서 / 컬럼에 대한 히스토리! 를 파악하는 것입니다.
데이터 다루는 건 클래스와 함수 하나하나 그 자체가 중요하다기 보다는 결국 인사이트를 가지고, 판단하는 것이
중요하다는 것이겠지요. 결국 데이터를 다루는 건 어떻게든 할 수 있으니까요.

         longitude    latitude  housing_median_age   total_rooms  \
count  3000.000000  3000.00000         3000.000000   3000.000000   
mean   -119.589200    35.63539           28.845333   2599.578667   
std       1.994936     2.12967           12.555396   2155.593332   
min    -124.180000    32.56000            1.000000      6.000000   
25%    -121.810000    33.93000           18.000000   1401.000000   
50%    -118.485000    34.27000           29.000000   2106.000000   
75%    -118.020000    37.69000           37.000000   3129.000000   
max    -114.490000    41.92000           52.000000  30450.000000   

       total_bedrooms    population  households  median_income  \
count     3000.000000   3000.000000  3000.00000    3000.000000   
mean       529.950667   1402.798667   489.91200       3.807272   
std        415.654368   1030.543012   365.42271       1.854512   
min          2.000000      5.000000     2.00000       0.499900   
25%        291.000000    780.000000   273.00000       2.544000   
50%        437.000000   1155.000000   409.50000       3.487150   
75%        636.000000   1742.750000   597.25000       4.656475   
max       5419.000000  11935.000000  4930.00000      15.000100   

       median_house_value  
count          3000.00000  
mean         205846.27500  
std          113119.68747  
min           22500.00000  
25%          121200.00000  
50%          177650.00000  
75%          263975.00000  
max          500001.00000  

print(test.describe())의 출력 결과입니다.
DataFrame의 통계적 요약 정보를 출력합니다.
Min / Max -> 9999999 / -9999999 / 이상한 문자로 된 값 ******** 이런 값들이 Min 이나 Max에 나오니 걸러낼 수 있습니다.
Mean -> 평균이 우리가 아는 수치와 비슷한지 확인할 수 있습니다.
25% 50% 75% -> 데이터가 편중되어 있는지 아니면 잘 분포되어 있는지 확인할 수 있습니다. -> 표준편차

# inplace 매개변수를 사용하지 않는 경우
# 판다스 대부분의 메서드에서는 기본적으로 데이터프레임의 복사본을 수정하고, 원본 데이터프레임은 그대로 유지
test = df.dropna()

# inplace=True를 사용하는 경우
#  이 매개변수를 True로 설정하면, 메서드는 데이터프레임의 복사본을 만들지 않고 원본 데이터프레임을 직접 수정
test.dropna(inplace=True)

위의 함수는 결측치가 나왔을때 데이터프레임의 원본을 유지하는지 직접 수정할 것인지를 설정할 수 있습니다.

import pandas as pd
import numpy as np

# 샘플 데이터프레임 생성
# 문자형 데이터는 describe() 사용해도 통계량이 나오지 않는다. -> count() 정도로 수만 셀 수 있다.
data = {'이름': ['홍길동', '임꺽정', None],
        '나이': [30, None, 40],
        '도시': ['서울', '부산', '광주']}
df = pd.DataFrame(data)

# 결측치가 포함된 데이터프레임 출력
print("원본 데이터프레임:\n", df)

# 결측치 제거
df.dropna(inplace=True) # inplace 잘못써서 원본 자체를 날릴수도 있다. 주의!!

# 결측치가 제거된 데이터프레임 출력
print("\n결측치 제거 후 데이터프레임:\n", df)

inplace 잘못써서 원본 자체를 날릴수도 있습니다. 주의가 필요합니다.


데이터프레임과 시리즈

# Pandas DataFrame 예시
import pandas as pd

df = pd.DataFrame({
    'Name': ['Alice', 'Bob', 'Charlie'],
    'Age': [25, 30, 35],
    'City': ['New York', 'Paris', 'Berlin']
})

# 각 속성 호출
print("Shape:", df.shape)
print("Data Types:\n", df.dtypes)
print("Index:", df.index)
print("Columns:", df.columns)
출력결과
Shape: (3, 3)
Data Types:
 Name    object
Age      int64
City    object
dtype: object
Index: RangeIndex(start=0, stop=3, step=1)
Columns: Index(['Name', 'Age', 'City'], dtype='object')

데이터프레임 df의 각 속성을 출력해서 볼 수 있습니다.

import pandas as pd

# 예시 데이터프레임 생성
data = {
    'Name': ['Alice', 'Bob'],  # 문자열 타입
    'Age': ['25', '30'],       # 숫자 데이터를 문자열로 표현
    'Member': ['True', 'False'],  # 불린 데이터를 문자열로 표현
    'Join Date': ['2021-01-01', '2021-07-01']  # 날짜 데이터를 문자열로 표현
}

df = pd.DataFrame(data)

# 결과 출력
print(df)
print(df.dtypes)

DB -> 문자열! -> 메모리 / 저장용량 효율적! 이라고 합니다. 강사님께서 확실하진 않다고 하셨지만^^
숫자나 날짜도 보통 데이터타입 보다 문자열로 되어 있는 경우가 많습니다.

# 데이터 타입 변환
df['Age'] = df['Age'].astype(int)  # 'Age' 열을 정수형으로 변환
df['Member'] = df['Member'].astype(bool)  # 'Member' 열을 불린형으로 변환
df['Join Date'] = pd.to_datetime(df['Join Date'])  # 'Join Date' 열을 datetime으로 변환

# 결과 출력
print(df)
print(df.dtypes)
결과 출력
    Name  Age  Member  Join Date
0  Alice   25    True 2021-01-01
1    Bob   30    True 2021-07-01
Name                 object
Age                   int64
Member                 bool
Join Date    datetime64[ns]
dtype: object

이렇게 열을 형변환 할 수 있습니다.

import pandas as pd
import numpy as np

# 예시 데이터프레임 생성
data = {
    'Name': ['Alice', 'Bob', 'Charlie'],
    'Age': [25, 30, 35],
    'City': ['New York', 'Paris', 'Berlin']
}
df = pd.DataFrame(data)

# 단일 열 선택 -> 결과는 Series
age_series = df['Age']
print(type(age_series))
출력결과 
<class 'pandas.core.series.Series'>
# 다중 열 선택 -> 결과는 DataFrame
subset_df = df[['Name', 'City']]
print(type(subset_df))
출력결과
<class 'pandas.core.frame.DataFrame'>

컬럼이 한개 있을때는 Series 클래스 이고, 컬럼이 두개 이상 있을때는 DataFrame 클래스입니다.
출력해보면 확실하게 알 수 있습니다.

# loc 사용 예시 - 변수명을 넣는다.
print(df.loc[:, 'Age'])
# iloc 사용 예시 - 위치를 넣는다.
print(df.iloc[:, 1])

loc[] 는 변수명을 넣어서 해당 컬럼을 출력할 수 있고, iloc[] 는 위치(숫자)를 넣어서 출력할 수 있습니다.
아무래도 loc[] 이 명시적이고 쓰기 좋을듯 합니다. iloc[]는 잘못 알고 있다면 원하지 않는 출력이 발생할 수 있습니다.

# 결측치 확인 isnull -> null 이 아니면 False, null 이면 True
print(df['Age'].isnull())

컬럼에 결측치가 있는지 확인하고, 결측치가 있을때 True를 반환합니다.
이 결과를 가지고 결측치를 포함할지 안할지 결정할 수 있겠지요?

# series 메서드 활용
import pandas as pd

# Series 생성
data = pd.Series([10, 20, 20, 30, 40, 30, 30, 15])

# sum(), mean(), value_counts() 사용 예시
print("Sum:", data.sum())           # 합계 series 에서만 작동
print("Mean:", data.mean())         # 평균 series 에서만 작동
print("Value Counts:\n", data.value_counts())  # 빈도 계산 series 에서만 작동

데이터 타입이 series 일 경우 사용 가능한 함수들입니다.
합계, 평균, 빈도계산 등은 컬럼이 하나여야 가능한 것들입니다.

# Series 와 DataFrame 을 다루는 메서드가 다름
import pandas as pd

# Series 생성
data = pd.Series([10, 15, 20, 25, 30, 20, 15])

# 다양한 메서드 활용
print("Minimum:", data.min())          # 최소값
print("Maximum:", data.max())          # 최대값
print("Standard Deviation:", data.std())  # 표준편차
print("Variance:", data.var())          # 분산 분산 에 root 씌운게 표준편차
print("Number of Unique Values:", data.nunique())  # 고유값 개수

# 데이터프레임 요약
df = pd.DataFrame({
    'Age': [25, 30, 35, 40, 45],
    'Salary': [50000, 55000, 60000, 65000, 70000]
})
print("\nDataFrame Summary:\n", df.describe())
print("\nDataFrame Info:")
df.info()

Series 와 DataFrame 은 다루는 메서드가 다릅니다.
Series 는 여러 메소드 통해서 하나하나 출력할 수 있고, DataFrame 은 info() 통해서 정보를 출력할 수 있습니다.

# CSV 파일 로드
file_path = '/content/sample_data/california_housing_test.csv'
test = pd.read_csv(file_path)

test_series = test['housing_median_age']
print(test_series)

# 각 요소에 1 더하기
stars_plus_one = test_series + 1
print("Stars Plus One:\n", stars_plus_one.head())

이렇게 컬럼의 각 요소의 값에 1을 일괄적으로 더할 수도 있습니다.

# 각 요소에 2 곱하기
stars_times_two = test_series * 2
print("Stars Times Two:\n", stars_times_two.head())

이렇게 2를 곱할 수도 있습니다.

# 30 보다 큰 나이 찾기
# 불린 연산이 가능하다 -> 해당하는 값만 가져올 수 있다.
high_age = test_series > 30.0
print("High age:\n", high_age.head())
High age:
 0    False
1     True
2    False
3    False
4    False
Name: housing_median_age, dtype: bool

결과가 불린 값으로 나와서 해당하는 값만 가져올 수도 있습니다.

import pandas as pd

df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9]
})

print(df)
print()

# 열 이름 변경
# columns({변경하기전 : 변경한후 , 변경하기전 : 변경한 후})
df = df.rename(columns={'A': 'X', 'B': 'Y', 'C': 'Z'})
print(df)
출력 결과
   A  B  C
0  1  4  7
1  2  5  8
2  3  6  9

   X  Y  Z
0  1  4  7
1  2  5  8
2  3  6  9

이렇게 열 이름을 변경할 수도 있습니다.

import pandas as pd

df = pd.DataFrame({
    'Name': ['Alice', 'Bob', 'Charlie'],
    'Age': [25, 30, 35]
})

# 'Salary'라는 새 열 추가
df['Salary'] = [50000, 60000, 70000]    # 다른 데이터 프레임을 가져오고 사용하는 경우 있음.
print(df)

이건 많이 사용하지 않는 다고 합니다. concat / join -> 데이터 결합을 많이 한다고 합니다.
기업데이터의 경우는 기존 데이터를 다루는 경우가 많고, 새로 생성하는 경우는 많지 않다고 합니다.

# 'Age' 열 삭제
df = df.drop(['Age', 'Name'], axis=1) # axis = 1 열/컬럼 의미 꼭 넣어줘야함(주의)
print(df)

drop 은 중요합니다.
첫번째로 ram 효율을 증대하기 위해서 import 할때만 전체를 가져오되 -> 작업을 할때는 필요한 변수만 가져와서 작업할 수 있습니다.
두번째로 머신러닝 / 딥러닝 ! -> 분석에 필요한 변수만 가져올때 사용할 수 있습니다.

import pandas as pd

# Sample DataFrames
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})

# 가로로 연결 (열 방향)
result = pd.concat([df1, df2], axis=1)

print(df1)
print()
print(df2)
print()
print(result)
   A  B
0  1  3
1  2  4

   A  B
0  5  7
1  6  8

   A  B  A  B
0  1  3  5  7
1  2  4  6  8

데이터 가로로 연결할 수 있는 방법입니다.

import pandas as pd

# Sample DataFrames with different column names
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'X': [5, 6], 'Y': [7, 8]})

print(df1)
print()
print(df2)
print()

# 가로로 연결 (열 방향) -  ignore_index=True 열 이름 다름
# 주의할 점 - df1과 df2가 동일한 행을 가지고 있을때!
# 동일하지 않을땐 join 이용
result = pd.concat([df1, df2], axis=1, ignore_index=True)

print(result)

가로로 연결 할때 동일한 행을 가지고 있을때 사용할 수 있습니다.
동일하지 않다면 join 등을 이용해야 합니다.

import pandas as pd

# Sample DataFrames
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})

print(df1)
print()
print(df2)
print()

# 세로로 연결 (행 방향)
result = pd.concat([df1, df2])  # 세로 연결할땐 axis=0 이고, 생략된 상태

print(result)
   A  B
0  1  3
1  2  4

   A  B
0  5  7
1  6  8

   A  B
0  1  3
1  2  4
0  5  7
1  6  8

데이터 세로로 연결할 수 있는 방법입니다. 아무래도 이걸 더 많이 사용하겠지요?


import pandas as pd

# Sample DataFrames with different index
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=[0, 1])
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]}, index=[2, 3])

print(df1)
print()
print(df2)
print()

# 세로로 연결 (행 방향) - 인덱스 다름
result = pd.concat([df1, df2], ignore_index=True)

print(result)

언제 사용할까요? 2023년 1월 ~ 2024년 1월 -> 분석 열심히 하고 있는데 2024년 2월 데이터가 신규로 오는 경우에
사용할 수 있습니다. 데이터컬럼이 동일할때 세로 결합이 유용합니다.

# 1. 조건에 대해서 / 내가 무엇을 할지에 대해서 잘 정의
# MAU -> 월별 활성화 고객수
# * 우리 웹에 1회 방문
# * 우리 윕에서 실제로 1회 구매한(유효고객)
# * 3회 이상 

#       방문횟수    로그기록    웹사이트활동
# 고객A    1
# 고객B    2
# 고객C    3

# pandas로 구현하는건 어렵지 않을 수 있다. 

# 2. 이 중에서 반복작업!
# -> 대시보드
# -> 반복문 / 함수! -> 프로그램화!

# 3. 반복이 되면 패턴!
# -> 패턴을 가지고 EDA 추가적으로 수행
# -> 해당 패턴이 왜 발생되었는가?

다시 한번 반복해서 말씀해주신 것 정리해보면 결국 기준을 어떻게 정의하고, 데이터를 사용하는지가 중요하다는 것입니다.
pandas로 구현하는 건 어렵지 않을 수 있고, 예쁘게 꾸밀 수 있지만 데이터가 정의가 잘 되어 있지 않거나 진짜 데이터가 아니라면
아무 의미가 없습니다.

이 이후에는 EDA 실습을 진행했습니다. 여러가지 배웠지만 코드는 짧아도 출력 결과를 여기에 다 쓰기는 좀 어려워서
과감하게 제외합니다.
nunique() 로 고유값 확인 할 수 있었고, 문자형을 숫자로 변경하는 방법 1. astype, 2. to_numeric을 배웠습니다.
그외 중복 제거, 이상치 제거 등을 이용해 박스플롯에 뿌려보는 것까지 해봤습니다.^^
이것들을 활용하면 멋진 admin페이지도 만들 수 있을 것 같습니다.
유의미한 데이터가 쌓이는게 더 중요하겟지만요.
이상입니다. 내일도 화이팅!!