교차 검증이 무엇인가요?

교차검증이란 모델 학습 시 데이터를 훈련용과 검증용으로 교차로 선택하는 방법

 

이전에 150개 중 120개만 데이터 훈련용, 30개를 검증용으로 사용했다면 단 1번만 훈련이 된다.

 

 

이를 보안하기 위하여 학습용 데이터 셋을 분할시켜 정확도를 올림.

 

00

예시로 150개의 자료가 있다면 대략 5등분 하여(유효한 수의 데이터셋을 유지한 채) 4 x Train(초록색)을 이용해서 학습을 시킨다음 1 x 검증용(파란색) 데이터로 정확도를 측정을 총 5번 반복해서 진행하는 과정이다.

 

 

교차 검증 왜 하나요?

 

장점 

  • 특정 데이터셋에 대한 과적합 방지
  • 더욱 일반화된 모델 생성 가능
  • 데이터셋 규모가 적을 시 과소적합 방지

 

단점

  • 학습 및 검증에 필요한 시간이 증가

 

코드를 통하여서 알아보자.

 

1. 주요 라이브러리 임포트

# 필수 라이브러리 호출


from sklearn.datasets import load_iris   # 품종 데이터셋
from sklearn.tree import DecisionTreeClassifier   # 의사결정나무 알고리즘
from sklearn.model_selection import KFold    # 교차검증 자료 나누기
from sklearn.metrics import accuracy_score as acc_sc   # 평가지표
from sklearn.model_selection import train_test_split   # 훈련, 검증용 자료 나누기

import numpy as np
import pandas as pd

2. 데이터 불러오기

# 데이터 재정의
iris = load_iris()
features = iris.data # X값
label = iris.target # y값
dt_clf = DecisionTreeClassifier(random_state=156)

# kfold에서 k= 5로 놓자. (5등분한다)
kfold = KFold(n_splits=5)


print('데이터 셋의 크기:', features.shape[0])

# 데이터 셋의 크기: 150

 

 

 

3. 학습 수행 

n_iter = 0
cv_accuracy = []  # CV : cross validation
for train_index, val_index in kfold.split(label):
    # Kfold.split()으로 반환된 인덱스를 통해 학습용, 검증용 데이터 정의
    X_val, X_train = features[val_index], features[train_index]
    y_val, y_train = label[val_index], label[train_index]
    # 각 CV별 학습 및 예측
    dt_clf.fit(X_train,y_train) # 학습
    pred = dt_clf.predict(X_val) # 예측
    # 정확도 지표 계산
    accuracy = np.round(acc_sc(y_val, pred),3)
    train_size = X_train.shape[0]
    val_size = X_val.shape[0]
    n_iter += 1
    print(f'\n #{n_iter} CV 정확도:{accuracy}, 학습데이터 크기:{train_size}, 검증데이터 크기 : {val_size}')
    print(f'#{n_iter} 검증 데이터셋 인덱스 :{val_index}')
    cv_accuracy.append(accuracy)
    
# 개별 iteration 별 정확도 평균내기
print(f'\n ## 평균 CV정확도 :{np.mean(cv_accuracy)}')

<정확표 값>

 

 #1 CV 정확도:1.0, 학습데이터 크기:120, 검증데이터 크기 : 30
#1 검증 데이터셋 인덱스 :[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29]

 #2 CV 정확도:0.967, 학습데이터 크기:120, 검증데이터 크기 : 30
#2 검증 데이터셋 인덱스 :[30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
 54 55 56 57 58 59]

 #3 CV 정확도:0.867, 학습데이터 크기:120, 검증데이터 크기 : 30
#3 검증 데이터셋 인덱스 :[60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
 84 85 86 87 88 89]

 #4 CV 정확도:0.933, 학습데이터 크기:120, 검증데이터 크기 : 30
#4 검증 데이터셋 인덱스 :[ 90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107
 108 109 110 111 112 113 114 115 116 117 118 119]

 #5 CV 정확도:0.733, 학습데이터 크기:120, 검증데이터 크기 : 30
#5 검증 데이터셋 인덱스 :[120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
 138 139 140 141 142 143 144 145 146 147 148 149]

 ## 평균 CV정확도 :0.9

 

 

cv_accuracy

# [1.0, 0.967, 0.867, 0.933, 0.733]

 

 

'AI > [ML]' 카테고리의 다른 글

[ML] 머신러닝 분류  (0) 2023.03.30
[ML] Scikit-learn 을 활용한 붓꽃 품종 예측하기  (0) 2023.02.08

Scikit-learn 을 활용한 붓꽃 품종 예측하기

 

 

sepal length, sepal , petal length, petal width를 통해서 label (품종)을 예측해보자

쉽게 말해서:  4개의 꽃의 특성을 구분한 150개의 자료가 있다.

특징1, 특징2....특징4(독립변수)와 거기에 맞는 품종이 표시된 120개의 데이터(특징 + 품종)를 이용하여 학습을 시킨 다음에 

나머지 30개의 특징만으로 그 특징에 맞는 품종을 예측을 하려고 한다.

 

 

 

 

 

어렵게 말하면 4개의 독립변수(특징)와 1개의 종속변수(품종)를 가지는 150개의 데이터를 가지고

120개는 훈련용 (4개의 독립변수 + 1개 종속변수) 

30개는 (4개의 독립변수) 검증용으로  1개의 종속변수 예측하기 :

 

 

어렵게 말하면 4개의 독립변수와 1개의 종속변수를 가지는 150개의 데이터를 가지고 1개의 종속변수 예측하기 :

 

scikit-learn 

# conda 가상환경시
#!conda install scikit-learn

!pip install scikit-learn

 

import sklearn 
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split

print(sklearn.__version__) # 사이킷런 버전확인

 

#붓꽃 데이터 세트를 로딩
iris=load_iris()

#iris.data는 Iris 데이터 세트에서 피처(feature)만으로 된 데이터를 numpy로 가지고 있음.
iris_data=iris.data # x값 들

#iris.target은 붓꽃 데이터 세트에서 레이블(결정 값)데이터를 numpy로 가지고 있다.
iris_label=iris.target #y값 들
print('iris target값:', iris_label)
print('iris target명:', iris.target_names) # ['setosa' 'versicolor' 'virginica'] 문자타입을 0, 1, 2숫자로 변환!

#붓꽃 데이터 세트를 자세히 보기 위해 DataFrame으로 변환한다.
iris_df = pd.DataFrame(iris_data,columns=iris.feature_names)
iris_df['label']=iris.target
iris_df.tail()
iris target값: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]
iris target명: ['setosa' 'versicolor' 'virginica']

# 0 : sentosa
# 1 : versicolor
# 2 : virginica

다음으로 학습용 데이터와 테스트용 데이터를 분리해보자. 학습용 데이터와 테스트용 데이터는 반드시 분리해야 한다.

학습데이터로 학습된 모델이 얼마나 뛰어난 성능을 가지는지 평가하려면 테스트 데이터 세트가 필요하기 때문이다.

이를 위해 사이킷런은 train_test_split()API를 제공한다. train_test_split()을 이용하면 학습 데이터와 테스트 데이터를 test_size 파라미터 입 값의 비율로 쉽게 분할이 가능하다. 예를 들어 test_size =0.2로 입력 파라미터를 설정하면 전체 데이터 중 테스트 데이터가 20%, 학습데이터가 80%로 데이터를 분할한다. 먼저 train_test_split()을 호출한 후 좀 더 자세히 입력 파라미터와 변환값을 살펴보자.

 

X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_label, test_size=0.2) 
# test_size=0.2 = 20프로는 테스트용이로 따로 빼두겠다

 

X_train.shape # (sepal length, sepal width, petal length, petal width)
# (120, 4)   120가지의 4개의 컬럼을 가지고 있고 이것을 학습용 x (독립 변수로 두겠다)

y_train.shape # (label => 품종이 어떤건지)
(120,)	     120가지의 품종이 있다 (종속 변수) 


# X_train의 4가지 독립변수들을 결합하여 y_train 종속 변수를 찾아내는 알고리즘을 학습시킴


# 그 알고리즘에 

X_test.shape # (sepal length, sepal width, petal length, petal width)
# (30, 4)   30가지의 4개의 컬럼을 직접 넣어서 

y_test.shape 
# (30,)      30개의 품종을 얼마나 잘 맞추는지 테스트하는것!

 

의사결정나무를 통해서 학습시키기

# 의사결정 트리에  120개의 학습용 데이터 넣기

DecisionTreeClassifier.fit(X_train,y_train)
#학습이 완료된 DecisionTreeClassifier 객체에서 테스트 데이터 세트로 예측 수행.

pred=dt_clf.predict(X_test) # X_test는 test_size=0.2에서 빼둔 (30, 4)개의 데이터

 

 

# 예측값
pred

# array([2, 2, 1, 1, 2, 0, 1, 0, 0, 1, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2, 1, 0,
#       0, 1, 0, 0, 2, 1, 0, 1])

 

# 실제값
y_test

# array([2, 2, 2, 1, 2, 0, 1, 0, 0, 1, 2, 1, 1, 2, 2, 0, 2, 1, 2, 2, 1, 0,
#       0, 1, 0, 0, 2, 1, 0, 1])

예측값(학습된 데이터로 만들어진 알고리즘) 30개중 2개가 실제값과 다름

정확도 : 0.93333

from sklearn.metrics import accuracy_score as acc_sc
print('예측 정확도: {0}'.format(acc_sc(y_test,pred)))


학습한 의사 결정 트리의 알고리즘 예측 정확도가 약 0.9333(93.33%)으로 측정되었다. 앞의 붓꽃 데이터 세트로 분류를 예측한 프로세스를 정리하면 다음과 같다.

  1. 데이터 세트 분리: 데이터를 학습 데이터와 테스트 데이터로 분리한다.
  2. 모델 학습: 학습 데이터를 기반으로 ML 알고리즘을 적용해 모델을 학습시킨다.
  3. 예측 수행: 학습된 ML 모델을 이용해 테스트 데이터의 분류 (즉, 붓꽃 종류)를 예측한다.
  4. 평가: 이렇게 예측된 결과 값과 테스트 데이터의 실제 결과를 비교해 ML 모델 성능을 평가한다.

'AI > [ML]' 카테고리의 다른 글

[ML] 머신러닝 분류  (0) 2023.03.30
[ML] 2. Cross Validation 교차 검증  (0) 2023.02.09

Matplotlib를 통한 시각화 첫 단계 

# matplotlib
!pip install matplotlib # 라이브러리 설치


import matplotlib.pyplot as plt # 라이브러리 임포트

 

 

값 채워 넣고 라이브러리 실행 (1차식)

data1 = [10,14,19,20,25]
plt.plot(data1)
plt.show()

 


 

2차식 넘파이를 활용하여 만들기

import numpy as np 
x = np.arange(-4.5, 5, 0.5) # 넘파이로 숫자 생성(-4.5 부터 5까지 0.5씩증가)
y = 2*x**2   # y = 2x^2
[x,y]



#[array([-4.5, -4. , -3.5, -3. , -2.5, -2. , -1.5, -1. , -0.5,  0. ,  0.5,
#         1. ,  1.5,  2. ,  2.5,  3. ,  3.5,  4. ,  4.5]),
# array([40.5, 32. , 24.5, 18. , 12.5,  8. ,  4.5,  2. ,  0.5,  0. ,  0.5,
#         2. ,  4.5,  8. , 12.5, 18. , 24.5, 32. , 40.5])]

시각화하기 

plt.plot(x,y)
plt.show()


 

여러 그래프를 하나의 창에 넣어보기

# 함수 설정하기
import numpy as np
import matplotlib.pyplot as plt

x = np.arange(-4.5, 5.0, 0.5)
y1 = 2*x**2
y2 = 5*x + 30
y3 = 4*x**2 + 10

 

# 시각화 방법 1
plt.plot(x,y1)
plt.plot(x,y2)
plt.plot(x,y3)
plt.show()

# 더 간단하게도 가능
#plt.plot(x,y1,x,y2,x,y3)
# plt.show()

 


여러 그래프를 여러 창에 넣어보기

# 넘파이로 함수 생성
import numpy as np

# 데이터 생성
x = np.arange(-5,5, 0.1)
y1 = x**2 -2

y2 = 20*np.cos(x)**2

#plt.figure 로 새창을 만들고 (숫자)로 여러 창 생성가능

plt.figure(1) # 1번 그래프를 창에 넣기
plt.plot(x, y1)

plt.figure(2) # 2번 그래프를 창에 넣기
plt.plot(x, y2)

plt.show()

'AI > [Visualization]' 카테고리의 다른 글

[Seaborn] Seaborn 그래프 형태  (0) 2023.02.13
[Matplotlib] Pie그래프  (0) 2023.02.13
[Matplotlib] 막대바  (0) 2023.02.13
[Matplotlib] 산점도 찍기  (0) 2023.02.13

import pandas as pd
df = pd.DataFrame({'Old A' : ['Old a1', 'Old a2', 'Old a3', 'Old a4'], 'Old B' : ['Old b1', 'Old b2', 'Old b3', 'Old b4'], 'Old C' : ['Old c1', 'Old c2', 'Old c3', 'Old c4']})
df

1. 인덱스 명 바꾸기

 

  • 1-1 df.index = ["바꿀 인덱스 값"]

# 모든 인덱스를 바꿀때만 

# 이미 생성된 컬럼수와 입력하는 컬럼수가 같아야함
df.index = ["New 0", "New 1", "New 2", "New 3"]
df

 

 

 

  • 1- 2 df.rename(columns = {"원래이름" : "바꿀이름" }, option)   

(일부의 인덱스명만 바꿀때 and 인덱스명으로)

 * rename의 파라미터인 딕셔너리 형태로 써줌

# 일부의 인덱스명만 바꿀때 and 인덱스명으로
df.rename(index = {0: "New 0", 1: "New 1"}) 
# df.rename({0: "New 0", 1: "New 1"}, axis =0)

# axis= 0 은 인덱스  (생략도 가능)

 

 

 

option : inplace = True (기존값도 변경)   

 

  • 1-3 df.index.values[컬럼번호] = "바꿀 컬럼명" 

(일부의 컬럼명만 바꿀때 and 컬럼명으로)

# 일부의 인덱스명만 바꿀때 and 인덱스 번호로
df.index.values[0] = "New A0"

 

 

 

오류발생 !!

# 'int' object has no attribute 'index'
# invalid literal for int() with base 10: 'New A0'

 

  • 1-4 컬렴을(열)을 인덱스로 만들기
df1 = df.set_index("Old A")
df1

 

 

 

2.  컬럼명 명 바꾸기

-> 인덱스랑 똑같다. 'Index 대신 'columns'로 바꾸면 끝

 

  • 1- 1  df.column 을 통해서 바꾸기  

(모든 컬럼명을 바꿔야 할때만)

* column 의 파라미터인 리스트형태로 써줌

df.columns = ["New A", "New B", "New C"] # 이미 생성된 컬럼수와 입력하는 컬럼수가 같아야함
df

 

 

  • 1- 2 df.rename(columns = {"원래이름" : "바꿀이름" }, option)   

(일부의 컬럼명만 바꿀때 and 컬럼명으로)

 * rename의 파라미터인 딕셔너리 형태로 써줌

# 일부의 컬럼명만 바꿀때 and 컬럼명으로
df.rename(columns = {"Old A": "New A1", "Old B": "New B1"}) 
# df.rename({"Old A": "New A1", "Old B": "New B1"}, axis =1)

# axis= 1 은 컬럼(열)

# rename() 함수에서는 lambda 사용가능
df.rename(columns= lambda x:'[' + x +']' )

 

option : inplace = True (기존값에 저장)   

 

  • 1-3 df.columns.values[컬럼번호] = "바꿀 컬럼명" 

(일부의 컬럼명만 바꿀때 and 컬럼명으로)

# 일부의 컬럼명만 바꿀때 and 컬럼 번호로
df.columns.values[0] = "New A1"

 

 

3.  데이터 값 변경

CRUD에서 R과 똑같음

https://itcreator.tistory.com/143

# https://itcreator.tistory.com/143    3-1번 

titan_df.loc[0,'Pclass']  # 3이 출력됐었음

# " = " 만 넣으면 끝
titan_df.loc[0,'Pclass'] = 999999  # 3에서 999999로 바뀜

 

 

4. 조건으로 데이터 값 변경하기 

- lamda

# st['default'] = st['default'].apply(lambda x : 0 if x == 'No' else 1)
# st

 

5. replace

df.replace('현재 문자열', '바꿀 문자열', inplace = True) # inplace = True : 원본도 바꿈


# 여러개 바꿀때는 Key-Value 형태로 가능 
df.replace({'현재 문자열1':'바꿀 문자열 1', '현재문자열2':' 바꿀 문자열 2'})


# 하나의 컬럼에서 바꿀때
df.replace({'컬럼이름': {'찾을 문자열': '대체 문자열'}}

DataFrame에서 데이터 추출

 

1. 추출용 데이터 셋 다운받기 

https://www.kaggle.com/competitions/titanic/data

에서 자료 다운받기 

 

# 데이터셋 불러오기

import pandas as pd
titan_df = pd.read_csv('./titanic/train.csv')
titan_df

 

 

2. 선택된 열(column) 세로만 뽑아보기

# 선택된 열(column) 세로만 뽑아보기

titan_df[['Name']]

# 여러개는 ,'컬럼명' 넣어주면 된다
# titan_df[['Name','Age']]


# print(type(titan_df[['Name']]))  => [[]] 2차원 <class 'pandas.core.frame.DataFrame'> 
# print(type(titan_df['Name']))   => [] 1차원 <class 'pandas.core.series.Series'>

 

 

 

2-1. 해당조건의 열(column) 세로 뽑아보기 

# 3. Pclass 1인 정보 추출하기

titan_df[titan_df.Pclass==1]  # <class 'pandas.core.series.Series'>
# titan_df[titan_df['Pclass']==1] # 이렇게 해도됨


#titan_df[titan_df.Pclass==1].Pclass.value_counts() # 갯수

# print(type(titan_df[titan_df.Pclass==1]))  =>  <class 'pandas.core.frame.DataFrame'>
# print(type(titan_df.Pclass==1]))           => <class 'pandas.core.series.Series'>

 

여러가지 조건 

# and 조건일 때는 &  /  or 조건일 때는 |   / Not 조건일 때는 ~
# 60세넘는  1등급 여성 추출

titan_df[(titan_df.Pclass==1) & (titan_df.Sex=='female') & (titan_df['Age'] > 60)]

# Not 조건인 경우 (반대조건)
# titan_df[~((titan_df.Pclass==1) & (titan_df.Sex=='female') & (titan_df['Age'] > 60))]

 

2-2 여러가지 기능들

# 해당 열(Name)에 글자가 포함된 데이터 출력
titan_df[titan_df.Name.str.contains('Mrs')]

# 인덱스번호 재정렬
titan_df[titan_df.Name.str.contains('Mrs')].reset_index()

# 인덱스번호 재정렬상태에서 상위3개만 보여주기 
# titan_df[titan_df.Name.str.contains('Mrs')].reset_index().head(3) # 상위 3개
# titan_df[titan_df.Name.str.contains('Mrs')].reset_index()[:3]  # 상위 3개

# titan_df[titan_df.Name.str.contains('Mrs')].reset_index().tail(3) # 하위


# 결측치받기
.isna() # 표로 보여주기
.isna().sum() # 합쳐서 보여주기

 

3. 선택된 행(row) 가로만 뽑아보기

titan_df.loc[[0]] # loc 이름이 0인 행만 추출해줘
titan_df.iloc[[0]] # iloc 위치가 0번째 행인 값들만 추출해줘

# 단일값을 불러오기1 - loc(label::명칭) // iloc(Positional-위치)

titan_df.loc[[0]] # loc 이름이 0인 행만 추출해줘
titan_df.iloc[[0]] # iloc 위치가 0번째 행인 값들만 추출해줘

# iloc 경우 슬라이스 가능
# iloc [시작:끝:옵션]

#  테스트하기 위해서 행 이름 바꿀때 
# data_df.rename(index={'two': 1, 'one': 3}, inplace=True) # inplace 원본도 바꿀래?
data_df

 

3-1. loc/iloc 을 통한 행열 을 사용하여 하나의 값 뽑기

 

titan_df.loc[0,'Pclass'] 
# 3 출력

 
# titan_df.loc[[0,'Pclass']] 로 하면 "['Pclass'] not in index" 에러 뜸

 

 

3-2. loc/iloc 을 통한 행열 을 사용하여 여러개의 값 뽑기

 

titan_df.loc[:,'Pclass':'Age']  
# 행 [:] = 전부 , 열 ['Pclass':'Age'] Pclass부터 ~ Age 까지3-1. loc을 통한 행열 을 사용하여 하나의 값 뽑기



titan_df.iloc[:,2:6]  
# 와 동일한 값 (2열부터 ~ 2,3,4,5 까지) 6열 포함x

 

<원본>

titan_df

 

 

 

 

 

<조건으로 나온 값>

 

DataFrame 행(row)열(column) 추가 삭제

CRUD중 Create 와 Delete

1: DataFrame 생성 또는 불러오기

# csv 불러오기

import pandas as pd

# 절대경로
# file_path = '/Users/chris/Desktop/Develop/TIL/python/BIGDATA/test.csv'
# tset_df = pd.read_csv(file_path, engine='python') # DataFrame의 약자 df

# 상대경로
test_df = pd.read_csv("./test.csv", encoding="cp949")  # ./ 현재폴더    # encoding="cp949" 한글 인코딩 오류해결용
# 생성하기

import pandas as pd

# 딕셔너리 형태로 
df = pd.DataFrame({'A' : ['a1', 'a2', 'a3', 'a4'], 'B' : ['b1', 'b2', 'b3', 'b4'], 'C' : ['c1', 'c2', 'c3', 'c4']})


# 리스트 형태로 (api로 불러올때는 대부분 이런식)
df = pd.DataFrame([{'A' : 'a1', 'B' : 'b1', 'C' : 'c1'},
		   {'A' : 'a2', 'B' : 'b2', 'C' : 'c2'},
                   {'A' : 'a3', 'B' : 'b3', 'C' : 'c3'},
                   {'A' : 'a4', 'B' : 'b4', 'C' : 'c4'}])
 


df

 

 

2: DataFrame  행(row) 추가 / 열(column) 추가

 

 

# 행(row, 가로) 추가 
df.loc[len(df)] = ['st', 123, '아무거나']     # len(df) df : 행 인덱스  (숫자넣는곳)
df

 

 

# 열(column, 세로) 추가

df['새컬럼'] = 'new'
df

 

 

3: DataFrame  행(row)  / 열(column) 삭제

 

# 행(row)가로 삭제

df2 = df.drop(0, axis = 0) # axis = 0 (생략가능) 행 
df2 

#df2 = df.drop([0, 1],axis = 0) 여러개도 한번에 가능
#df2

# df.drop([0], axis = 0, inplace = true) # inplace = True / 리턴없이 원본이 수정됨 (결과창 안뜸)
# df.drop([0], axis = 0, inplace = false) # false(생략가능)인 경우 리턴은 있지만 원본은 그대로 (결과창 뜸)

# 원본을 저장하고 싶으면 =>
# df = df.drop([0], axis = 0, inplace = false) 로  df에 담아야함

# df = df.drop(df.index[[0, 1]]) #인덱스 번호로도 삭제가능 (행 이름이 문자일때 번호로 지우기용)

# 열 (column, 세로) 삭제

df2 = df.drop('A', axis = 1)  # axis = 1 열 
df2

Pandas 시리즈(Series)와 데이터프레임(Dataframe)

Pandas의 차원이해하기 

<class 'pandas.core.series.Series'> : 1차원

<class 'pandas.core.frame.DataFrame'> : 2차원

 

 

 

 

 

 

 

DataFrame

:  시리즈가 여러개 합쳐진 자료형
데이터프레임에서 하나의 행 또는 열만 뽑으면 그것은 시리즈이다.

열내의 데이터 같고, 열간의 데이터는 다름

 

 

 

 

차원에 대한 이해

import pandas as pd

dic = {'컬럼_열1': ['행_가','행_나','행_다'], '컬럼_열2': ['행_a', '행_b', '행_c'], '컬럼_열3': ['행_1', '행_2', '행_3']}


df = pd.DataFrame(dic)
df

pd.DataFrame(df[df.컬럼_열1=='행_가']['컬럼_열2'])

# [][] =>  [행][렬] 
#  [행] => [df.컬럼_열1=='행_가']  => '행_가'가 포함된 행_가, 행_a, 행_1을 뽑는다. 0번째 행을 뽑는다.
#  [열] => ['컬럼_열2'] => 행_a, 행_b, 행_c를 뽑고 곂치는 값을 출력한다.
#  행_a 출력

 

 

 


pd.DataFrame(df[df.컬럼_열2=='행_c']['컬럼_열3']) 
#을 한다면?

# 행_3 출력

 

행렬 가로 세로 도대체 무엇이 맞는가?

 

분명 이런사진을 본다면 마치 행이 세로 / 열이 가로 인 것처럼 보이지만 

 

 

행은 row = 가로

가로방향으로 값을 넣는다!! -> 값을 다 넣었으면 행축세로방향으로 늘어난다!

 

행 = 가로 = row = axis 0 (데이터를 넣는 방향)

행축(행이 증가하는방향) = 세로방향으로 증가

 

 

열은 column = 세로

 세로방향으로로 값을 넣는다!! -> 값을 다 넣었으면  로방향으로 늘어난다!

 

열 = 세로 = column = axis 1 (데이터를 넣는 방향)

열축(열이 증가하는방향) = 가로방향으로 증가

 

 

가로인지 세로인지는 데이터를 넣는 방향을 말하는것!

 

행열은 축 번호를 부르는것!

 

들어가기 전

프로그래밍 언어 추천받기 전,

내가 왜 코딩을 하려하는지, 무엇을 만들려고 하는지에 대한 기본적 방향은 잡혀있어야 합니다.

프론트엔드가 뭔지, 백엔드 정도는 알아야 언어를 추천받을 수 있습니다.

 

0: 개발직군에 대한 이해

개발에는 다양한 분야가 있습니다:

임베디드, 웹개발, 앱개발, 데이터분석 등으로 나눠지며 이 안에서도 또 세분화되며 세분화된 분야마다 다양한 특징들을 가집니다. 

 

개발이 처음이시라면 웹개발을 추천합니다!

앱이 더 낫지 않나요? 웹을 이해한다면 앱은 금방 배울 수 있으며, 앱에서도 웹개발 기술을 사용합니다.

웹개발은 비교적 낮은 단계의 수준 (네트워크 이해, 데이터베이스) 개발 난이도이지만 굉장히 많은 수요를 가집니다.

 

웹개발에는 주로

Python, Java, Javascript 를 사용하여 위 3가지 언어에 대해서만 살펴보겠습니다.

 

임베디드, VR(AR) : c++, c#

앱개발 : swift(ios), kotlin(android)

 

 

 

1: Java (백엔드) 

어려운 정도 : ★★★

취업진입 : ★★★☆

일자리수 : ★★★★★

대한민국은 자바 공화국이라고 불립니다. 대부분 전자정부프레임워크를 자바로 통일하여 자바만 알아도 신입으로 빠르게 취업이 가능합니다.

일자리가 엄청 많아서 빠른 취업이 가능합니다. 제대로 된 3년 정도 경력을 쌓는다면 이직도 편합니다.

백엔드를 하신다면 반드시 자바를 알고 계시는 게 좋습니다.

 

2: Javascript (프론트, 백엔드)

어려운 정도 : ★★☆

취업진입 : ★★★★

일자리수 : ★★★☆

* java랑은 아무런 관계가 없습니다. 사자와 바다사자 관계입니다.

유일한 프론트엔드 언어입니다. 백엔드(node.js)도 가능하며, 미디어아트(three.js , p5.js) 등 에도 사용되는 스크립트언어입니다.

백엔드 개발자도 제2언어로 배울정도로 프론트에선 javascript 가 유일하며, 활용도가 높습니다.

컴퓨터 비전을 공부 중인데 DL활용 웹 제작에는 파이썬 장고나, 자바스크립트 노드.js를 주로 쓴다고 합니다.

비전공신입취업 시장에서는 백엔드보다 프론트가 취업하기 유리합니다. (시장이 굉장히 빠르게 바뀝니다)

개인적으로 미래를 보고 웹 개발을 한다면 자바 보다는 자바스크립트를 추천하는 바 (23년 3월 기준).

 

 

 

 

3: Python (데이터, 백엔드)

어려운 정도 : ★★

취업진입 : ★★

일자리수 : ★★★ ~ ★★★☆

데이터-통계분야 (머신러닝, 딥러닝, AI)라면 반드시 배워야 하는 언어입니다.

백엔드(flask, Django) 가능하지만 주로 스타트업에서 사용합니다(대부분 경력직) 

이쪽 분야는 대게 석사 이상을 요구하고 일자리수가 적어 취업 진입이 쉽지 않습니다. 

(언어는 쉽지만 사용할 라이브러리가 수학적 지식을 많이 요함)

하지만, 언어가 쉽고 굉장히 간결하여서 코딩테스트용으로 많이 쓰입니다.

 

미래를 본다면 점점 더 각광받는 언어가 될 거라 생각합니다.

 

 


 

 

결론

나는 취업을 위해 코딩을 배울 것이다 : 자바

나는 프론트엔드로 취업을 할 것이다 +  도저히 뭘 배워야 할지 모르겠다 : 자바스크립트

나는 취미로 배우겠다 + 데이터 쪽으로 일할 것이다 : 파이썬

 

 

 

 

 

'P-Language' 카테고리의 다른 글

[객체지향] 객체지향의사실과오해  (0) 2023.02.07

게시물 내렸습니다.

우측 하단 챗봇으로 문의 주세요!

 

비전공자

순공 2시간 1주 컷가능

RGB 대신 BGR을 쓰는 이유

1: 사진, 영상에서는 당연히 RGB 순서로 사용하는데 CV에서는 왜 BGR을 사용할까?

 

구글링을 했더니  사타야 말릭 박사(Dr. Satya Mallick) 의 글을 찾을 수 있었습니다. 
본문을 요약하면 사타야 말릭 박사는 한 컨퍼런스에서 OpenCV의 창시자인 그레이 브라드스키 박사(Dr. Gray Bradski)를 만났고 그에게 왜 RGB가 아닌 BGR을 쓰냐고 물었고 그에 대한 대답은 “왜 미국 철도 표준이 4피트 8.5 인치 인가?”라는 질문으로 되돌아 왔다고 합니다. 
이게 무슨 말이냐면 미국이 철도 표준을 정할때 무슨 대단한 이유가 있어서 4피트 8.5인치로 정한것이 아니라 열차 이전에 주로 사용했던 마차의 폭에 마춰서 정했다는 겁니다. 결국, 브라드스키 박사도 BGR로 정한데는 별다른 이유가 있던게 아니라 그걸 정하던 시절 초창기 카메라 산업 개발자들이 BGR을 많이 썼기 때문에 그 자신도 그냥 아무 생각없이 BGR을 선택했다는 거죠.
 

2: 그럼 왜  영상 기기 개발자들 사이에서는 RGB보다 BGR을 많이 사용했을까?

 
이는 엔디안(Endianness)와 관련이 있습니다.
CPU에 따라서 값을 메모리에 저장하고 읽을 때,  MSB 즉 큰 값을 먼저 쓰는 빅엔디안과 LSB 즉 작은 값을 먼저 쓰는 리틀엔디언 방식이 있는데, 인텔에서 만든 x86계열의 CPU는 리틀엔디언을 따릅니다.
(OpenCV는 인텔에서 시작)
 
예를 들어 0x123456이라는 값을 빅엔디언으로 저장하면 0x123456 순으로 저장되지만 리틀엔디언으로 저장하면 0x563412순으로 저장되게 됩니다.
BGR 포맷으로 이루어진 색상 정보는 각 채널당 8비트로 이루어 지고 3개의 채널을 사용하므로 24비트 즉, 0xBBGGRR가 필요합니다.  24비트 단위의 입출력은 효과적이지 않으니 메모리에 저장할때 unsigned 32비트를 사용하는것이 일반적인데 이렇게 되면 0x00BBGGRR 가 됩니다. 이 값을 x86 계열의 CPU 처럼 리틀엔디언을 사용해서 저장하면 메로리에는  0xRRGGBB00가 저장됩니다.

 

이렇게 저장된 값을 메모리에 직접 접근해서 읽어 들이는 디스플레이 장치, 카메라 등의 영상 하드웨어에서는 CPU와 무관하게 지정된 엔디안으로 앞에서 부터 읽어 들이면 자연스럽게 RGB로 읽히기 때문에 BGR 포맷이 초창기 영상 기기 관련 하드웨어 개발자들에게 자연스레 널리 사용되었다고 볼 수 있습니다.
BGR 포맷과 엔디안과의 관계에 대한 추가적인 의견은 아래의 스택오버플로우 링크에서도 보 실 수 있습니다.
 

 

파이썬 + 셀레니움을 활용한 크롤링 (나사이미지)

https://eol.jsc.nasa.gov/SearchPhotos/photo.pl?mission=ISS068&roll=E&frame=41445 

 

 

Astronaut Photo ISS068-E-41445

Spacecraft nadir point: 17.8° S, 71.2° W Photo center point: Photo center point by machine learning: Nadir to Photo Center: Spacecraft Altitude: 226 nautical miles (419km)

eol.jsc.nasa.gov

 

 

0) Selenium 간단 설명

https://selenium-python.readthedocs.io/getting-started.html

 

 

 

1) 준비물

1 :  크롬드라이버

자신에 맞는 드라이버 다운로드 : https://chromedriver.chromium.org/downloads

내가 쓰는 크롬 버전 확인 : 크롬 우측 상단 점3개 > 설정 > 크롬정보

 

 2 :  작업용 폴더

바탕화면에 작업용 폴더 만들고 드라이버를 그 폴더에다가 넣어준다.

 

3: 셀레니움 설치

pip install selenium

 

 

2) 코드작성

1: 임포트하기 

from selenium import webdriver # 웹열때
from selenium.webdriver.common.keys import Keys # 키 가져오기용
from selenium.webdriver.common.by import By # Xpath이용
import time # 슬립걸때 사용
import urllib # 다운로드

 

 

2: 코드 생각해보기 

 

# 41445 번부터 41558번 까지 다운받고싶음 
# https://eol.jsc.nasa.gov/SearchPhotos/photo.pl?mission=ISS068&roll=E&frame=41445 여기에 들어가서
# 큰 화소수 다운 받아야함   => class="btn btn-light" 버튼 눌러서 모달창 띄우기 
# 모달창에서 이미지주소 긁어서 urllib으로 다운로드함 

# 다운받았으면 페이지 번호 1올려야함 => num += num을 반복 -> while문 써야함

 

 

눌러야하는 부분 xpath 확인하기

 

# 41445번째 xpath : //*[@id="wrap"]/div/div/div[4]/div[1]/a[1]

# 41446번째 xpath : //*[@id="wrap"]/div/div/div[4]/div[1]/a[1]

각 페이지별 고화소 픽셀 위치선택하는 xpath는 동일함

 

# 41445번째 xpath : //*[@id="myModal1"]/div/div/div[2]/img
# 41446번째 Xpath : //*[@id="myModal1"]/div/div/div[2]/img   

각 페이지 모달창에 뜬 사진의 xpath가 동일함

=> Xpath 고정값으로 두고 페이지의 num만 바꾸면 됨

 

 

 

 

3: 코드 작성해보기

# 41445 번부터 41558번 까지 다운받고싶음 
# https://eol.jsc.nasa.gov/SearchPhotos/photo.pl?mission=ISS068&roll=E&frame=41445 여기에 들어가서
# class="btn btn-light" 버튼 눌러서 모달창 띄우기 
# 모달창에서 이미지주소 긁어서 urllib으로 다운로드하기 
# 41445번째 xpath : //*[@id="myModal1"]/div/div/div[2]/img
# 41446번째 Xpath : //*[@id="myModal1"]/div/div/div[2]/img   xpath는 동일하니 num만 바꾸면 되겠네?
# 다운받았으면 num += num을 반복 -> while문 써야함


# 초기번호 페이지번호설정 

num = 41445  # 명시적으로 page_num으로 하는게 좋으나 간단하게 num으로 설정해둠

#화면열기
driver = webdriver.Chrome() # while 안에 넣어버리면 계속 새창이 뜸


while num < 41559 :
    print(num)

    # 다운받을 화면
    driver.get('https://eol.jsc.nasa.gov/SearchPhotos/photo.pl?mission=ISS068&roll=E&frame={0}'.format(num))
    time.sleep(2.1)
    print("화면열기 성공", num)

    # 화소선택버튼 찾기
    search = driver.find_element(By.XPATH, '//*[@id="wrap"]/div/div/div[4]/div[1]/a[1]')
    search.click()
    print("화소선택 버튼 누리기 성공", num)

    # image 주소 가져오기
    img_url = driver.find_element(By.XPATH, '//*[@id="myModal1"]/div/div/div[2]/img' ).get_attribute('src')
    print("주소 가져오기 성공", num)

    # image 다운로드
    urllib.request.urlretrieve(img_url, str(num)+'.png')
    print("다운로드 성공", num)
    time.sleep(5) #다운받는 시간 확보
    num = num + 1
 
    print(num)

 

 

다음에는 24장 모아서 1초짜리 영상으로 자동화 해봐야겠다

24fps 영상 자동화

 

원리:
인터넷이 가능한 블랙박스로 자동신고

조건 :
1. 차량안에 네트워크 구축
2. 국민신문고 DB접근 api 필요
3. 자동신고 어플
4. 관련 처벌법안 필요
5. 80km/h 이상 주행 가능시 해당 조건 유효

실행:
1차로 주행시 정속주행 차량이 있다면:
- 일정 거리를 두고 왼쪽 깜빡이
- 녹화실행
- 1분이상 비키지 않는다면 녹화저장
- API를 통해 국민신문고 DB로 쏘기





https://missing-semester-kr.github.io/2020/version-control/

1. Untracked :  인지는 했지만 관리는 안함  or 관리 목록에서 내림

2. Tracked 

  • Unmodified (수정된 상태가 아닌)
  • Modified (수정된 상태)
  • Staged 

 

 

 

 

첫 상태 : 인지는 했지만 관리는 안함  [U]

 

 

 

 

Readme.md -> "add . readme.md" -> staged 상태로 [A]

 

 

 

 

 

 

commit -> unmodified 상태 [흰색상태]

 

 

파일 수정-> modified 상태 [M]

 

git commit 다시하면 다시 unmodified 

 

 

 

git log 깃로그 (자주 쓰는 옵션 --oneline -- graph)

 

 

 

 

터미널 편하게 쓰기 + 

 

1. terminal > cnt + r > i search  + 검색어 :  썻던 코드중 에서 검색 찾아줌

 

 

2. 단축키 설정 : 

  • 1. profile 폴더생성
    • (win) touch ~/.bash_profile 
    • (mac) touch ~/.zsh_profile 
  • 2. profile 열기 
    • (win) code ~/.bash_profile
    • (mac) open ~/.zsh_profile 
  • 3. 단축키 등록 ( 맥 oh-my-zsh 이미 설치했다면 아마 등록 되있음)
    • alias gl = 'git log --oneline --graph'
    • alias jn = 'jupyter notebook'

 

 

 

 

 

 

'협업툴 > [GIT]' 카테고리의 다른 글

[GIT] .gitignore 활용법  (0) 2023.01.17

 

1.  1:1 관계

결혼관계테이블 : 남자 1 , 여자 1

사원증테이블 : 사원번호 1, 사원 1

주민등록번호테이블 : 번호 1, 사람 1 

  • 하나의 레코드가 다른 테이블의 레코드 한 개와 연결된 경우이다.

 

 

 

2.  1:N 관계

 

: (나의)게시글 테이블

유저 1명, 게시글 N개

 

: (나의)강의테이블

학생 1명, 강의 N개 

 

: (카트속) 아이템

 

: (게시글에) 댓글

 

: 나라의 정부기관

 

 

 

이런식으로 테이블 2개를 활용해서 표현 가능함

 

 

 

 

 

 

3.  N: M 관계

https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/ 

 

https://cloudstudying.kr/lectures/501

Q: 수강신청 테이블을 만들려고 한다 어떻게 하면 좋을까?

과목에는 성과 사랑, 통계물리 수업이 있고, 5명이 수업을 들을려고 한다.

  성과 사랑 통계물리
홍길동 V --
김철수 -- V
손흥민 -- V
리처드파인만 -- V
아인슈타인 -- V

라고 만들면 -- 빈칸이 생기게 된다 -> 무결성 위배 (유저와, 과목테이블을 관계활용한 수강신청이라는 테이블을 하나 더 생성해야함)

 

----------------------------------------

N:M 관계 예시

 

: 좋아요 테이블(게시글 테이블과 비교)

좋아요는 여러명이 누름, 한 사람은 여러개의 좋아요 가능

하트는 여러개의 게시글에 들어간다,  

어떤 게시글인지, 어떤 유저 눌렀는지

 

: 대여테이블

책은 여러명에게 대여가능, 한 사람은 여러개 책 예약가능

어떤 책인지, 어떤 사람인지

 

: 수강신청테이블 (강의 테이블과 비교)

수강은 여러명에게 등록가능, 한 사람은 여러개 강의 등록가능

어떤 강의인지,  어떤학생이 듣는지 

 

 

: 좋아요 버튼 

 

: 투표

 

 

.gitignore 왜 필요한가?

git에 올라가면 안될 중 DB 정보나 KEY 등과 같은 중요한 정보나 , 깃관리가 필요없는 데이터를 사전에 무시하기 위해사용함 

 

 

1. .gitignore 파일 생성

touch .gitignore  # 보통 프로젝트 만들때 가장 먼저 만듬

 

 

2.  .gitignore 작성 (중요파일)

만든 .gitignore을 열어서 무시할 파일 이름을 넣으면 끝

중요한 정보가 secret.txt에 있다.
.gitignore에서 파일이름을 넣으면 끝

 

 

2-1.  .gitignore 작성 (중요폴더)

만약에 폴더 전체를 ignore 하고싶다면?!

**/Directory명/ 을 적어주면 끝

 

 

3. .gitignore 작성 (불필요파일)

 

gitignore.io

자신이 쓰는 운영체제, 개발환경, 프로그래밍언어를 넣어주면 알아서 만들어줌

 

 

 

 

 

이렇게 나오면 ctrl + A , ctrl + C 해서 .gitignore 폴더에 붙여넣기 

 

 

 

 

 

 

'협업툴 > [GIT]' 카테고리의 다른 글

[GIT] 버전관리 + 터미널 편하게 쓰기  (0) 2023.01.19

http://raccoonyy.github.io/django-annotate-and-aggregate-like-as-excel/

 

(엑셀만큼 쉬운) Django Annotation/Aggregation

Django ORM을 강력하게 만들어주는 기능 중 하나는 바로 애너테이션(annotate)과 애그리게이션(aggregate)입니다. 이 두 기능이 명쾌하게 와닿지 않아서 사용하지 못하다가, 엑셀에 빗대어 이해해보니

blog.raccoony.dev

 

 

https://docs.djangoproject.com/en/4.1/ref/models/querysets/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

1:1

1:N  (=N:1)

N:N 구분해서 공부하기

 

이전 Signup에는 Default User를 사용함 이번 버젼은 User를 커스텀하겠음

 

 

결과적으로 이것만 추가됨

 

from django import forms
drom django.contrib.auth.forms. import UserCreationForm
from django.contrib.auth import get_user_model # user 정보 알아서 빼주는 함수

# models.py 에서 Foreignkry가 user 일때만 세팅스.어스_유저_모델
# accounts/urls.py
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    mbti = models.CharField(max_length=4)
    # 아무값 채워넣으면 됨
    # 아무값 채워넣으면 됨



# Settings 맨밑에다 이거 추가
AUTH_USER_MODEL = 'accounts.User'

 

https://docs.djangoproject.com/en/4.1/topics/auth/default/ 

 

 

 

 

 

 

1:N 관계

User-table 하나에서 여러개의 Question-table, 여러개의 reply-table 생성가능

User-table : Question-table = 1 : N

User-table : reply-table.      = 1 : N

 

Question-table : reply-table = 1:N

 

 

<User-table>

id(seq) username password mb

  ti
1 홍길동 1234  
2 민수 123  
3 민아 1234  

<Question-table>

id(seq) Question user_id
1 오늘의 메뉴는? 1
2 오늘 날씨는? 2
3 내일 뭐하지? 3

<reply-table>

id 내용 Que_id user_id
1 햄버거 1(오늘메뉴는?) 3 (민아)
2 맑음 2 1 (홍길동

 

 

 

 

 

 

https://itcreator.tistory.com/129

이어서 하기

 

이전버전은 USER는 DB를 만들지 않아도 따로 생성되었음 (물론 커스터마이징 가능하지만 그건 나중에)

이번은 이전설정과 거의 비슷하게 한번 더 앱을 만들어 보고 게시판/댓글 DB 까지 다뤄보기

 

1.  기초설계

1. ctrl + shift + ~ 터미널 창 열어주기

2. board 앱 폴더 생성 및 등록

# root 기준  터미널 명령어

python manage.py startapp <app 이름>

mkdir -p <app 이름>/templates/<app 이름>  # 템플릿 생성

cd  <app 이름>/templates/<app 이름>  # 최하단 까지 이동

touch detail.html form.html index.html

#detail 게시판글 자세히보기  
#form 게시판 작성 폼
#index 게시글 리스트

 

3. urls.py 및 forms.py 생성

# 다시 최상단 으로 돌아와서( 보통 cd .. 3번 하면됨)

Touch <app이름>/urls.py    # (..../<app이름> 으로 들어오는 통로 열어주기)

Touch <app이름>/forms.py   

# Form 이 친구는 왜만드는거임?
   # 1. 유효성 체크 (validation check)
   # 2. HTML 안에 인풋태그 만들기 귀찮 
   # 3. 저장할 때 request.POST에서 일일이 꺼내기 귀찮아서
   # 이런 작업을 Form 하게 만들려고 미리 폴더 생성

 

2.  board  > urls.py 세팅해주기    (앱이름을 board 로 할 경우)

from django.urls import path
from . import views

app_name = 'board'
urlpatterns = [
    # articles/create/
    path('create/', views.create_article, name='create_article'),    # url에 articles/create/으로 접근이 온다면 => views 에 있는 create_article 함수 실행 / 그리고 이 과정을 create_article 이 라고 칭하겠다. 
    # articles/
    path('', views.article_index, name='article_index'),
    # articles/1/
    path('<int:article_pk>/', views.article_detail, name='article_detail'),
    # articles/1/update/
    path('<int:article_pk>/update/', views.update_article, name='update_article'),
    # articles/1/delete/
    path('<int:article_pk>/delete/', views.delete_article, name='delete_article'),

 

 

3. Model 단 (board > models.py)

from django.db import models
from django.conf import settings

class Article(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # settings.AUTH_USER_MODEL 파이썬이 이렇게 써달래
    title = models.CharField(max_length=100)									# on_delete=models.CASCADE 이게 지워지면 안에 다른것도 다 지워달라 
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

class Comment(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    content = models.CharField(max_length=200)
    # foreign key 쓰면 필드명에 _id 붙이지 않기.
    # migrate 하면 알아서 뒤에 _id가 붙음
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    Created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

 

 

3-1 . Form 단 (board > forms.py)

from django import forms
from .models import Article, Comment
class ArticleForm(forms.ModelForm):
    
    class Meta:
        model = Article
        #fields= '__all__'
        exclude = ('user',) # user는 Article의 FK라 article테이블의 user의 키가 
        					# User테이블의 pk값이 없는걸 넣을려고 하면 DB에서 막음 (무결성)
       
class CommentForm(forms.ModelForm):
    content = forms.CharField(min_length=2, max_length=200,
    widget=forms.TextInput(attrs={'autofocus': True})
    )
    class Meta:
        model = Comment
        # fields = ('content',)
        exclude = ('article', 'user')

model. form 끝냈으면 마이그레이트 시작

python manage.py makemigrations board

python manage.py migrate board

 

 

4. Control 단 (board > views.py)

1. 쉬운 버전용 필요한 클래스 import 하기

# 화면구성, 화면이동, 객체를 받거나 없으면 404에러를 터트려줌 (이거 없으면 서버 문제가 아닌데도 500 서버단 에러를 터트림)
from django.shortcuts import render, redirect, get_object_or_404

# get, post등 url 접근 방식에 따라 분류해서 허용하겠다
from django.views.decorators.http import (require_http_methods, require_POST, require_safe, )

# 로그인 상태에만 해당 함수 실행가능
from django.contrib.auth.decorators import login_required

# DB 불러오기
from .models import Article, Comment

# 앞서 만듬 form 불러오기
from .forms import ArticleForm, CommentForm

 

2. create_article 작성

@login_required  # 로그인이 되어야 이 작업가능
@require_http_methods(['GET', 'POST'])  # GET, POST로 들어오는 경우
def create_article(request):   #request 요청 받은 정보 전부 인수로 전달한뒤 함수 실행
    
    if request.method == 'POST': # POST라면(게시글 등록버튼 누를때)
        form = ArticleForm(request.POST) # POST로 쏘는 내용물 채워서 form에 저장
        if form.is_valid():  # 값이 유효하다면
            article = form.save(commit=False) # 잠깐 저장은 하지말아봐  # article부터 만들어야하는데 save를 하면 오류터짐
            article.user = request.user  # 페이징 요청자의 정보를 글작성자의 정보로 넣어줌
            article.save()  # 게시글 저장(내장함수)
            return redirect('board:article_detail', article.pk)   # 글번호와 함께 detail() 함수실행

    else:   # GET으로 왔다면(페이지를 요청할때)
        
        form = ArticleForm()   # form.py에서 만든걸 form으로
    context = {'form': form }    # context에 담아서 
   
    return render(request, 'board/form.html', context)  # form.html로 넘겨줌

 

3. read

@require_safe # get, head 요청만 허용하겠다.
def article_index(request):
    articles = Article.objects.all()   # DB의 article의 모든 값을 articles에 넣어줌
    context = {'articles': articles, }   # 그걸 context에 담아서
    return render(request, 'board/index.html', context)   # index.html로 넘겨줌

@require_safe
def article_detail(request, article_pk):   # 몇번째 게시글인지 알 수 있게 article_pk 받아야함
    article = get_object_or_404(Article, pk=article_pk)  # db에서 객체를 받던가 404를 띄워라! why? DB에 없는걸 요청한다면 니 잘못
    form = CommentForm() # 댓글보여줄려고-> detail.html에 board/_comment_form.html에 form 쓸려고
    context = {     # 게시글 정보와 댓글을 context에 담아서
        'article': article,
        'form': form
    }
    return render(request, 'board/detail.html', context)   # detail.html로 보냄

 

4. update

@login_required  # 로그인 필요
@require_http_methods(['GET', 'POST'])   # GET, POST로 오는 경우
def update_article(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)   # 받은 번호의 게시글 정보를 DB에서 찾아 article에 넣어줌
   
    if request.user != article.user:			 # 작성자 아니면
        return redirect('board:article_detail', article.pk)    # 게시글 번호와함께 detail.html로 보내버림

    if request.method == 'POST':   # POST인 경우(게시글 수정 버튼을 누를때)
        form = ArticleForm(request.POST, instance=article)  # instance=student 가 없으면 수정이 아니라 새 값으로 저장되어버림 + 이전정보 넣어주기
        if form.is_valid():  # form이 유효하다면
            # 기존에 저장된 user_id 갱신할 필요가 없기때문에 commit=False 필요 X
            article = form.save()   # 저장해서 article에 넣어주기
            return redirect('board:article_detail', article.pk)   # 게시글 번호 주면서 detail() 함수실행
    else:   # get방식이라면 (이 페이지를 요청할때)
        form = ArticleForm(instance=article)   # # instance=student => form에다가 이전 정보 넣기
    context = {'form': form}   # context에 담아서
    return render(request, 'board/form.html', context)   # form.html로 보냄

 

5. Delete

@login_required   # 로그인 필요
@require_POST   # POST인 경우(url로 들어오는거 막겠다/ 제출버튼으로 토큰 던지면서 와라)
def delete_article(request, article_pk):  # 요청받은 정보와 게시글 번호를 주면서
    article = get_object_or_404(Article, pk=article_pk)  # 번호에 맞는 Article DB에서 객체를 꺼내서 article에 주던가 404로 
    if request.user == article.user:   # 요청자와 글쓴이가 같다면 
        article.delete()   	# 게시글 삭제(내장함수) 
        return redirect('board:article_index')   # index로 보내주기
    else:     # 요청자와 글쓴이가 같지않다면
        return redirect('board:article_detail', article.pk)  # 게시글 번호와함께 detail.html로 보냄

 

6. 댓글 (간단하게 CRD만)

@require_POST
def create_comment(request, article_pk):  # 요청 정보와 게시글 번호를 받음
    form = CommentForm(request.POST) # 댓글 제출 버튼시 값들을 form에 넣어줌 
    article = get_object_or_404(Article, pk=article_pk) # 받은 번호의 게시글 정보를 DB에서 찾아 article에 넣어줌

    if form.is_valid():  # form이 유효하다면
       
        comment = form.save(commit=False)    # 완전 저장시 NOT NULL 에러 뜨니까, 직전에 멈춰 주세요.
        comment.article = article       # 여기선 알아서 값 넣어줌
        comment.user = article			 # 여기선 알아서 값 넣어줌
        comment.save()		# 값 저장(내장함수)
    return redirect('board:article_detail', article.pk)  # 게시글 번호 받아서 detail() 함수 실행 
    # comment.article.pk 게시글 번호임

@require_safe # get, head 요청만 허용하겠다.
def article_index(request):   # 요청한값 받음
    articles = Article.objects.all()  Article DB 값 받아서 articles에 넣어줌
    context = {'articles': articles, }   # 그 값을 context 넣고
    return render(request, 'board/index.html', context)  # index.html로 보내줌

@require_safe
def article_detail(request, article_pk):   # 요청정보와 게시글 번호 받음
    article = get_object_or_404(Article, pk=article_pk)  # 받은 번호의 게시글 정보를 DB에서 찾아 article에 넣어줌
    form = CommentForm() # 댓글보여줄려고-> detail.html에 board/_comment_form.html에 form 쓸려고
    context = {
        'article': article,
        'form': form
    }
    return render(request, 'board/detail.html', context)


@login_required
@require_POST
def delete_comment(request, article_pk, comment_pk):
    comment = get_object_or_404(Comment, pk=comment_pk)
    article = get_object_or_404(Article, pk=article_pk) # article.pk 를 쓰기위해서 만듬
    
    if request.user == comment.user:
        comment.delete()
    return redirect('board:article_detail', article.pk)
    
    # article_pk 안쓰는 이유는 사용자를 믿을수없어서.
    # 사용자가 보낸거 _pk 구분한다음  내부적으로 확인해서 믿을하면 .pk 써도 무방

 

 

총정리

@login_required
@require_http_methods(['GET', 'POST'])
def create_article(request):
    
    if request.method == 'POST': 
        form = ArticleForm(request.POST) 
        if form.is_valid():
            article = form.save(commit=False)
            article.user = request.user 
            article.save()
            return redirect('board:article_detail', article.pk)

    else: 
        form = ArticleForm()
    context = {'form': form }
    return render(request, 'board/form.html', context)

@login_required
@require_http_methods(['GET', 'POST'])
def update_article(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    
    if request.user != article.user:
        return redirect('board:article_detail', article.pk)

    if request.method == 'POST':
        form = ArticleForm(request.POST, instance=article)        
        if form.is_valid():
           
            article = form.save()
            return redirect('board:article_detail', article.pk)
    else:
        form = ArticleForm(instance=article)
    context = {'form': form}
    return render(request, 'board/form.html', context)

@login_required
@require_POST
def delete_article(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    if request.user == article.user:
        article.delete()
        return redirect('board:article_index')
    else:
        return redirect('board:article_detail', article.pk)



@require_POST
def create_comment(request, article_pk):
    form = CommentForm(request.POST)
    article = get_object_or_404(Article, pk=article_pk)

    if form.is_valid():
      
        comment = form.save(commit=False) 
       
        comment.article = article
        comment.user = article
        comment.save()
    return redirect('board:article_detail', article.pk)
  

@require_safe 
def article_index(request):
    articles = Article.objects.all()
    context = {'articles': articles, }
    return render(request, 'board/index.html', context)

@require_safe
def article_detail(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    form = CommentForm() 
    context = {
        'article': article,
        'form': form
    }
    return render(request, 'board/detail.html', context)



@login_required
@require_POST
def delete_comment(request, article_pk, comment_pk):
    comment = get_object_or_404(Comment, pk=comment_pk)
    article = get_object_or_404(Article, pk=article_pk)
    
    if request.user == comment.user:
        comment.delete()
    return redirect('board:article_detail', article.pk)

 

+ Recent posts