데이터프레임과 시리즈

 

데이터프레임이란?(Dataframe)

  • 데이터 분석에서 가장 중요한 데이터 구조
  • 관계형 데이터베이스의 테이블 또는 엑셀 시트와 같은 형태 (2차원 구조)
  • 변수들의 집합 → 각 열을 변수라고 부름

시리즈란?(Serise)

  • 하나의 정보에 대한 데이터들의 집합
  • 데이터 프레임에서 하나의 열을 떼어낸 것.(1차원)

 

<선언 및 확인>

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import koreanize_matplotlib # 한국어를 사용하게 해줌
import seaborn as sns

import joblib
import warnings

warnings.filterwarnings(action='ignore') # 경고 메시지 무시
%config InlineBackend.figure_format='retina' # 고화질의 결과물 출력
#딕셔너리 생성
dict1 = {'Name': ['Gildong', 'Sarang', 'Jiemae', 'Yeoin'],
        'Level': ['Gold', 'Bronze', 'Silver', 'Gold'],
        'Score': [56000, 23000, 44000, 52000]}
        
df = pd.DataFrame(dict1)

# 확인
print(df.head()) #head()안에 숫자가 없으면 기본으로 5가 들어감
display(df)

 

<데이터 읽어오기>

# 데이터 읽어오기
path ='https://raw.githubusercontent.com/DA4BAM/dataset/master/Attrition_simple2.CSV'
# path = 'airquality_simple.csv'
data = pd.read_csv(path)
# 상위 5개 확인
print(data.head(5))

 

자주사용하는 메서드( 배열명.메서드() )

  • 데이터명.head(): 상위 데이터 확인
  • 데이터명.tail(): 하위 데이터 확인
  • 데이터명.shape: 데이터프레임 크기
  • 데이터명.values: 값 정보 확인(저장하면 2차원 numpy 배열이 됨)
  • 데이터명.columns: 열 정보 확인
  • 데이터명.columns.values : array 형태로 확
  • 데이터명.dtypes: 열 자료형 확인
  • 데이터명.info(): 열에 대한 상세한 정보 확인
  • 데이터명.describe(): 기초통계정보 확인(count, mean, std, min, 25%, 50%, 75%, max)
    • 데이터명.describe().T - 전치행렬로 보기 편해진다.
  • 메이터명.corr() : 상관관계를 나타냄
    • numeric_only = True을 하면 수치형만 분석해줌
    • corr().style.background_gradient() 가독성을 높여줌
  • 데이터명.isna() : 결측치 확인
  • 데이터명.isna().sum() : NaN데이터가 있는지 확인 (값이 NaN인 데이터를 합쳐서 보여줌)
  • 데이터명[데이터명.isnull().any(axis=1)] : 결측치가 있는 행만 확인
  • 데이터명.copy()깊은복사 (원본을 헤치지 않을때 사용 -> 결과가 맘에들면 다시 원복)
  • 데이터명.dropna() : NaN인 애들을 없앰 axis를 지정
  • 데이터명.fillna() : Nan인 애들을 값을 지정해서 채움
  • 시리즈명.mode() : 최빈값 확인, 시리즈의 형태로 (index 최빈값) 형태
    • 시리즈명.fillna( 시리즈명.mode()[0] ) - 다음과 같은 형태를 활용해야 최빈값 지정 가능
    • mode()와 mode()[0]  각각 출력해보길 권장
  • 시리즈명.ffill() : 변수 NaN 값을 바로 앞의 값으로 채우기
  • 리즈명.bfill() : 변수 NaN 값을 바로 뒤의 값으로 채우기
  • 데이터명.drop_duplicates() : 중복된 행을 제거해 줌
    • inplace = True,

<단순 조회>

  • 데이터명[ '열 이름' ] - 시리즈로 조회
  • 데이터명[ [ '열 이름' ] ] - 데이터프레임으로 조회

 

DF.sort_values

<정렬해서 보기 / 중요!>

  • 데이터명.sort_values(by = [ '열1'], ascending = [ True ] ) 
    메소드로 특정 열을 기준으로 정렬한다.
  • 데이터명.sort_values(by = [ '열1', '열2' ],ascending = [ True, False ])
    다양한 값과 정렬방식 가능
  • 데이터명.ascending 옵션을 설정해 오름차순, 내림차순을 설정할 수 있다.
  • 데이터명.sort_values( [ '열1'], ascending = [ False ] )  by는 생략할 수 있다. 
    • ascending=True: 오름차순 정렬(기본값)
    • ascending=False: 내림차순 정렬

reset_index

  • 데이터명. reset_index(drop = True) - Index로 설정된 열을 DF내에서 삭제할 물어본다. True = 삭제, False = 유지
  • 데이터명. reset_index(inplace = True) - 원본 객체를 변경 하는지 물어본다. True = 적용, False = 미적용

 

<기본 집계>

  • 데이터명[ ' 이름' ].unique()
    메소드로 고유값을 확인하며, 결괏값은 배열 형태가 된다.
  • 데이터명[ ' 이름' ].value_counts()
    메소드로 고유값과 그 개수를 확인하며, 결괏값은 시리즈 형태가 된다.
  • .sum() 합 / .max() 최값 / .min() 최솟값 / .mean() 평균 / .median() 열 중앙값 
  • 데이터명.describe() / count, mean, std, min, 25%, 50%, 75%, max

 

LOC(isin / between)

<조건으로 조회 .loc / 매우중요!!!>

  • df.loc[조건] 형태로 조건을 지정해 조건에 만족하는 데이터만 조회할 수 있다.
  • 우선 조건이 제대로 판단이 되는지 확인한 후 그 조건을 대 괄호 안에 넣으면 된다.

 

  • 데이터명.loc[ 데이터 [ '열 이름' ] > 10 ]  #열 값이 10보다 큰 행 조회
  • 데이터명.loc[( 데이터 [ '열 이름1' ] > 10) & ( 데이터 [ '열 이름2' ] == 4) ] 
    # 열1 값이 10보다 크고 열2의 값이 4인 데이터 조회
  • 데이터명.loc[( 데이터 [ '열 이름1' ] > 10) | ( 데이터 [ '열 이름2' ] == 4) ] 
    # 열1 값이 10보다 크거나 열2의 값이 4인 데이터 조회
  • 데이터명.loc[( 데이터 [ '열 이름1' ].isin( [ 1, 4 ] ) ] # isin( [ 데이터 값 ] )
    안에 있는 데이터 값인 데이터 조회(↑  같은 의미)
  • 데이터명.loc[(배열명[ '열 이름1' ].between( 25, 32 ), inclusive = 'both' ]
    # 값1 ~ 값2 범위 안의 데이터 조회 inclusive 생략시 기본값 'both' ( 값1 <= 데이터 <= 값2 ) 'left', 'right', neither'

 

inclusive 생략시 기본값
'both'
값1 <= 데이터 <= 값2  'left' 값1 <= 데이터 < 값2 
'right' 값1 < 데이터 <= 값2  'neither'  값1 < 데이터 < 값2 

 

 

 

<조건에 만족하는 행의 일부열 조회 / 많이쓰임!>

  • 데이터명.loc[배열명[ '열 이름' ] >= 10000, [ '열 이름1' ] ] 
    # 조건에 맞는 하나의 열 조회
  • 데이터명.loc[배열명[ '열 이름' ] >= 10000, [ '열 이름1', '열 이름2', '열 이름3 ] ]  
    # 조건에 맞는 여 열 조회
  • ex1) data.loc[data['MonthlyIncome'] >= 10000, ['Age', 'MaritalStatus', 'TotalWorkingYears']]
  • ex2) data.loc[(data["Age"].between(50, 59)) & (data["MonthlyIncome" <10000),
    ['Age', 'MonthlyIncome', 'Gender', 'DistanceFromHome']]

 

<데이터프레임 집계>

  • 데이터명[ '열 이름' ].sum()
  • 데이터명[ '열 이름1', '열 이름2' ].mean()
  • 데이터명.groupby( 데이터 ['열 이름1'], as_index = True )[ '열 이름2' ].mean()
    # 열1을 기준으로한 열2의 평균(결과값)
    • DF.reset_index()를 통해 DF 구조를 맞춰줄 수 있음  
    • DF.columns.name=None : 열이름에 대한 이름을 제거합니다.  
  • as_index= 그 목록을 index로 쓸거야? 물어보는것
  • [ '열 이름2' ].mean() = 시리즈로 조회 / [ ['열 이름2'] ].mean() = 데이터프레임으로 조회
  • 데이터명.groupby( '열 이름1', as_index = True )[ ['열 이름2', '열 이름3', '열 이름4'] ].mean() #여러 열 집계
  • 여러개의 열을 집계할 경우 [  ] 리스트로 묶어주어야 된다.
  • 데이터명.groupby( '열 이름1', as_index = True )[ ['열 이름2'] ].agg( [ 'min', 'max', 'mean'] )
  • agg()를 활용해 여러 함수로 한꺼번에 집계

 

<데이테프레임 변경>

  • 열 이름 변경

DF.rename( columns = { 현재 열이름1 : 바꿀 열이름1, 현재열이름2 : 바꿀 열이름2 }, inplace = True)

# rename() 함수로 열 이름 변경
#inplace=True 변경할 colums를 저장 / False면 바꾼상태로 조회만 적용x
path = 'https://raw.githubusercontent.com/DA4BAM/dataset/master/Attrition_simple2.CSV'
data = pd.read_csv(path)  

data.rename(columns={'DistanceFromHome' : 'Distance', 
                    'EmployeeNumber' : 'EmpNo',
                    'JobSatisfaction' : 'JobSat',
                    'MonthlyIncome' : 'M_Income',
                    'PercentSalaryHike' : 'PctSalHike',
                    'TotalWorkingYears' : 'TotWY'}, inplace=True)
                    
# 확인
data.head()

 

  • 모든 열 이름 변경

DF.columns = [ 바꿀 이름1, 바꿀 이름2 ]

# 모든 열 이름 변경 / 열 수에 맞게 변경
data.columns = ['Attr','Age','Dist','EmpNo','Gen','JobSat','Marital','M_Income', 'OT', 'PctSalHike', 'TotWY']

# 확인
data.head()

 

 

<열 추가>

데이터명[ '새로운 열 이름' ] = 가공할 데이터

  • 올해 급여 : MonthlyIncome(M_Income)
  • 급여 인상율 : PercentSalaryHike(PctSalHike)
  • 작년 급여 * ( 1 + PctSalHike / 100 ) = M_Income
  • 작년 급여 = M_Income / (( 1 + PctSalHike / 100 )
# final_amt 열 추가
data['Income_LY'] = data['M_Income'] / (1+data['PctSalHike']/100 )
data['Income_LY'] = round(data['Income_LY'])
# 확인
data.head()

 

 

DF.drop

<열 삭제>

  • 데이터명.drop( '열 이름', axis= n , inplace = T/F) 
    메소드를 사용해 열을 삭제합니다.
    • DF = DF.drop( '열 이름', axis= n ) - 이 방식은 새로운 DF를 만듦으로 inplace를 사용하면 안된다.
    • DF.drop( columns = [ ], inplace = T ) - axis를 지정 안하고 columns지정해도 됨 (axis가 1일 경우)
  • axis=0: 행을 의미 / 삭제(기본 값)
  • axis=1: 열을 의미 / 삭제
  • inplace=True 
    옵션을 지정해야 실제로 반영이 됩니다. (False : 삭제한 것 처럼 보여줘(조회!))
  • 데이터명.dropna( axis= n , inplace = T) - 데이터 값이 NaN인 데이터를 삭제하고 적용
    • axis = 0 이라면 행을 날림
    • 시리즈를 사용해 특정 열만 NaN을 없앤다면
    • DF.열이름 = DF.열이름.dropna(axis = n)으로 사용
  • 조건에 맞을 경우에 그 행만 삭제하고 싶은 경우
    • index_to_drop = df[df[ '열 이름' ] 조건 ex) > 4000 ].index
    • df = df.drop(index_to_drop)
    • 또는 df = df[df[' 열 이름 '] != 기준] 등으로 조건으로 저장
# 잘못 되었을 경우 되돌리기 위해 data를 복사합니다.
data2 = data.copy()

# 열 하나 삭제
data2.drop('Income_LY', axis=1, inplace=False) # 실제로는 삭제안됨
data2.drop('Income_LY', axis=1, inplace=True) # 실제로 삭제됨

# 확인
data2.head()

# 열 두 개 삭제
data2.drop(['JobSat2','Diff_Income'], axis=1, inplace=True)

# 확인
data2.head()

 

DF.fillna

  • 데이터명.fillna( 채울 값 inplace = T/F)
    • 시리즈를 사용해 특정 열의 NaN만 채운다면
    • DF.열이름 = DF.열이름. fillna ( 채울 값 )으로 사용
    • method='bfill' : 변수 NaN 값을 바로 뒤의 값으로 채우기
    • method='ffill' : 변수 NaN 값을 바로 앞의 값으로 채우기

DF.ffill / DF.bfill

  • 이터명.ffill() / 데이터명.bfill() 
  • 시리즈의 경우
    • DF[열이름] = DF[열이름].ffill() : 변수 NaN 값을 바로 앞의 값으로 채우기
    • DF[열이름] =DF[열이름].dfill() : 변수 NaN 값을 바로 뒤의 값으로 채우기

DF.interpolate

  • 시리즈의 경우
    • DF[열이름] = DF[열이름].interpolate(method = 'linear' ) : 선형보간법으로 채우기 (중간값)
    • DF[열이름] = DF[열이름].interpolate(method = ' nearest ' ) : 가까운 값으로 채워줌

 

pd.cut

<값 변경>

  • 데이터명[ '열 이름' ] = 0 단순변경
  • 데이터명.loc[ 데이터명[ '열 이름' ]  < 조건문, '열 이름' ] = 0
  • 데이터명[ '열 이름' ]  = np.where( 데이터명[ '열 이름' ] > 40, 1, 데이터명[ '열 이름' ] )
    # 조건이 맞으면 1, 아닐시 다시 그 데이터 입력
  • 데이터명[ '열 이름' ]  = 데이터명[ '열 이름' ] .map({ '요소1' : 1, '요소2' : 0}) / 범주형 데이터에 사용 가능

 

<숫자형 -> 범주형 변수로 변환>

  • 새로운 범주 데이터명 = pd.cut( 데이터명[ '열 이름' ] , 3, labels = ['a','b','c'])
    pd.cut(숫자형변수, 분할방식, 범주값)

 

  • 내가 원하는 구간 자르기 : bins = []
    • ex) pd.cut(data2['Age'], bins =[0, 40, 50, 100] , labels = ['young','junior','senior'])

 

pd.concat / pd.merge

<데이터프레임 결합>

  • pd.concat() - 매핑 기준 : 인덱스(행), 칼럼이름(열) / 데이터프레임 구조에 맞게 함침
    • pd.concat([ 합칠 데이터1, 데이터2, 데이터3], axis = 0/1 , join ="조인 종류") 
      join 생략시 outer join 
    • 방향 axis = 0 - 세로()로 합치기(위, 아래로 붙여라) - 칼럼 이름 기준
    • 방향 axis = 1 - 가로()로 합치기(옆으로 붙여라) - 행 인덱스 기준
    • join = ‘outer’ : 모든 행과 열 합치기 (기본값) 
    • join = ‘inner’ : 같은 행과 열만 합치기
  • pd.merge() - 매핑 기준 : 특정 칼럼(key)의 값(열 값) 기준으로 결합 / 데이터 값 기준으로 합침
    • pd.merge( DF1, DF2, how = 'inner', on = '요소(공통된 열 이름)') /
      how 생락시 inner join, on 생략시 None으로 알아서 공통을 찾음
    • 옆으로만 병합 
    • how = 'inner' : 같은 값만
    • how = 'outer' : 모두
    • how = 'left' : 왼쪽 df는 모두, 오른쪽 df는 같은 값만
    • how = 'right' : 오른쪽 df는 모두, 왼쪽 df는 같은 값만

pd.get_dummies

  • pd.get_dummies( DF, columns = [ ] , dtype = int) - 가변수화
    • ★ drop_first=True  - 가변수화한 첫 열을 없앤다. -> 다중공선성 문제를 없앤다.

 

pd.pivot

  • 데이터명.pivot(index = , columns = , values = ) / 결합은 아니지만 데이터를 재구성 해줌
  • 원하는 데이터의 열을 index, columns, values로 지정하여 새로운 DF으로 생성

 

pd.melt

  • 데이터명.melt(id_vars = '', value_vars = '' )
    • id_vars - 기준되는 열을 지정
    • value_vars  - 값과 같이 행으로 들어갈 열
    • 두 값 모두 리스트로 여러개 지정 가능

 

<시계열 데이터>

  • 행과 행에 시간의 순서(흐름)가 있고 
  • 행과 행의 시간간격이 동일한 데이터

 

<날짜 요소 뽑기> 

  • 날짜 타입으로 변환한 후 .dt.날짜요소로 추출 가능
구분 air['Date'].dt air['Date'].dt.isocalendar().
day X
month Y
year year
주차 x week
요일 weekday : 0~6 day : 1~7

 

< 시간에 따른 흐름 추출하기 : Time Lag >

  • 데이터명[ '새로운 열' ]  = 데이터명[ '열 이름' ].shift(n)
    - 시계열 데이터에서 시간의 흐름 전후로 정보를 이동시킬 때 사용
    shift()의 default 값은 1, shift(2) 2행 밀기, shift(-1) 뒷쪽으로 밀기
  • 데이터명[ '새로운 열' ]  = 데이터명[ '열 이름' ].rolling(n, min_periods = 1).mean()
    - 시간의 흐름에 따라 일정 기간 동안 평균이동하면서 구하기
    - min_periods =1 -> 데이터가 하나라도 존재하면 조회함.
  • 데이터명[ '새로운 열' ]  = 데이터명[ '열 이름' ].diff(n) - 특정 시점 데이터, 이전시점 데이터와의 차이 구하기

 

MinMaxScaling

from sklearn.preprocessing import minmax_scale

minmax_scale(데이터) 를 통해 데이터를 스케일링 할 수 있다.

 

 

 

import 선언

# scikit-learn를 별칭(alias) sk로
import sklearn as sk

#Pandas를 사용할 수 있도록 별칭(alias)을 pd로
import pandas as pd

# 그래프를 그리기 위한 선언
# 필요하면 설치 !pip install seaborn
import seaborn as sns
import matplotlib.pyplot as plt

# split을 위한 선언
from sklearn.model_selection import train_test_split

# 스케일링을 위한 선언
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)


# numpy 선언
import numpy as np

#라벨 인코딩
from sklearn.preprocessing import LabelEncoder

#원한 인코딩의 경우에는 그냥 .get_dummies()
df = pd.get_dummies(df, columns=['범주형 열'], drop_first=True)

# import 
from sklearn.metrics import * # 모든 함수 로드

# 분류 모델 평가 지표
from sklearn.metrics import accuracy_score 		# 정확도
from sklearn.metrics import precision_score 	# 정밀도
from sklearn.metrics import recall_score 		# 재현율
from sklearn.metrics import f1_score 			# f1 score
from sklearn.metrics import confusion_matrix 	# 혼동행렬
from sklearn.metrics import accuracy_score 		# 모든 지표 한번에

# 회귀 모델 평가 지표
from sklearn.metrics import r2_score			# R2 결정계수
from sklearn.metrics import mean_squared_error	# MSE
from sklearn.metrics import mean_absolute_error	# MAE

# 머신러닝 모델 정리
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier # 분류
from sklearn.tree import DecisionTreeRegressor # 회귀
from sklearn.ensemble import RandomForestClassifier # 분류
from sklearn.ensemble import RandomForestRegressor # 회귀

!pip install xgboost
from xgboost import XGBClassifier # 분류
from xgboost import XGBRegressor # 회귀

!pip install lightbgm
from lightgbm import LGBMClassifier # 분류
from lightgbm import LGBMegressor # 회귀

 

원핫 인코딩 및 라벨링

#원핫 인코딩 예시
oh_cols = ['Address1', 'Address2']
df_preset = pd.get_dummies(df_del, columns = oh_cols, drop_first = True)

#라벨링 예시
from sklearn.preprocessing import LabelEncoder

# 객체 생성
le = LabelEncoder()

# 인코딩 적용
df['범주형 열'] = le.fit_transform(df['범주형 열'])

 

x, y 분리

from sklearn.model_selection import train_test_split
x = df_preset.drop(columns = ['y컬럼명'])
y = df_preset['y컬럼명']
X_train, X_valid, y_train, y_valid = train_test_split(x, y, test_size = 0.2, random_state = 42)

 

머신러닝 예제

!pip install lightgbm
from lightgbm import LGBMClassifier
lgbm = LGBMClassifier(n_estimators=3, random_state=42)  
lgbm.fit(X_train, y_train)

from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report

y_pred = lgbm.predict(X_valid)
cm = confusion_matrix(y_valid, y_pred)
sns.heatmap(cm, annot=True)

print(classification_report(y_valid, y_pred, zero_division=1))

 

딥러닝 예제

 

이진 분류

model = Sequential()
model.add(Dense(64, activation='relu', input_shape=(X_train.shape[1],)))
model.add(Dropout(0.2))
model.add(Dense(32, activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='adam', 
              loss='binary_crossentropy', 
              metrics=['accuracy']) 

es = EarlyStopping(monitor='val_loss', patience=5) 

checkpoint_path = 'best_model.keras'
mc = ModelCheckpoint(checkpoint_path, monitor='val_loss', verbose=1, save_best_only=True)

history = model.fit(X_train, y_train, epochs=30, batch_size=16,
                   validation_data = (X_valid, y_valid),
                    callbacks=[es, mc]
                    )

 

다중 분류

n = x_train.shape[1]

clear_session()

model = Sequential([Input(shape=(n, )),
                   Dense(64, activation='relu'),
                   Dropout(0.2),
                   Dense(32, activation='relu'),
                   Dropout(0.2),
                   Dense(16, activation='relu'),
                   Dropout(0.2),
                   Dense(2, activation='softmax')])

model.compile(optimizer= Adam(learning_rate = 0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# EarlyStop
min_de = 0.001
pat = 5
es = EarlyStopping(monitor = 'val_loss', min_delta = min_de, patience = pat)

# MCP
cp_path = 'best_model.keras'
mcp = ModelCheckpoint(cp_path, monitor='val_loss', verbose = 1, save_best_only=True)

history = model.fit(x_train, y_train, epochs = 10, batch_size=10, validation_split=0.2, callbacks = [es, mcp]).history

history = model.fit(x_train, y_train, epochs = 10, batch_size=10, validation_split=0.2).history

 

데이터 분석의 큰 그림 : CRISP-DM

 

출처 : CRISP-DM IT위키

이를 분석하면 다음과 같이 표로 확인할 수 있다.

CRISP-DM은 데이터를 이용한 비즈니스 문제 해결 방법론이다.

큰 회사들은 이러한 프로세스를 기반으로 둔 자체적인 프로세스를 통해 비즈니스를 한다.

Ai개발자 지망생은 이후 Ai를 개발하면서 기업의 비즈니스를 해결하는 역할을 할 수 있다.

단계 절차 세부활동 비고 간단히
1 BusinessUnderstanding 업무 목표 수립, 현재 상황 평가마이닝
목표 수립, 프로젝트 계획
  무엇이
문제인가?
2 DataUnderstanding 초기 데이터 수집데이터 기술 데이터 품질
검증
진행이 불가할 경우 ①로 회귀 어디?
진짜?
3 DataPreparation 데이터 설정, 선택데이터 생성, 정제 데이터
통합, 데이터 클렌징
  모델링을 위한
데이터 구조
준비
4 Modeling 모델링 기법 선택테스트 설계 생성 모델
생성 및 평가
진행이 불가할 경우 ②로 회귀 학습, 검증
(예측, 평가)
5 Evaluation 결과 평가프로세스 재검토 진행이 불가할 경우 ①로 회귀 문제가
해결되는가?
6 Deployment 전개 계획 수립모니터링 및 유지 계획 최종
보고서 작성
   

 

 

분석할 수 있는 데이터는 다음 질적테이터와 양적테이터로 나뉜다.

흔히 정성적과 정량적 데이터라고 말하는데 처음 보는 사람은 이를 구분하는게 쉽지 않다.

예를 들어 '3월'이라는 데이트를 갖고있다면 이것은 정성적 데이터일까? 정량적 데이터인가?

간단하게 구분하는 방법은 '3월이 1월의 3배인가?' 생각해 보면 된다. 판매량 매출액 같은 정량적 데이터는 A가 B의 n배인가?라는 질문에 위화감이 없을 거다.

그러나 예로 든 3월이 1월의 3배인가?를 들었을 때 우리의 뇌는 이상함을 감지하게 된다. 이렇게 단순히 몇 배인가? 물어볼 수 없는 데이터를 정성적 데이터로 구분할 수 있다. 즉, 월에 관련된 데이터는 정성적 데이터, 질적 데이터이다.

 

 

분석할 수 있는 데이터의 기본은 2차원이다.

흔히 우리가 행과 열의 형태로 Table을 만드는 DB작업을 할 때에도 이러한 형태를 갖는다.

 

분석할 수 있는 정보의 종류 2가지

  • 수치형 (숫자)
  • 범주형 (범주)

두 가지 종류가 특별한 구조를 가져야 함

  • 기본 구조 : 2차원
    • 행 : 분석단위, 데이터 한 건, 한 건
    • 열 : 정보, 변수, 요인(x, feature, input / 독립변수), 결과(y, target, label, output / 종속변수)

(분석, 모델링을 위한) 데이터 구조를 다루는 패키지

  • numpy
  • pandas

 

 

<Numpy>

우리는 python에서 리스트를 많이 활용한다. 다른 타입의 데이터도 한꺼번에 저장 가능하고, 요소를 변경, 추가, 제거하기가 요이하기 떄문이다.

그러나 데이터분석에서는 수학적 계산이 가능해야 되고 대량의 데이터를 처리하는 속도가 빨리야 한다.

numpy는 빠른 수치 계산을 위해 C언어로 만들어진 python 라이브러리고 벡터와 행렬 연산에 편리한 기능들을 제공한다.

 

# 별칭 없이 라이브러리 불러오기
import numpy
a = numpy.array([1, 2, 3, 4, 5])
# 별칭을 주고 라이브러리 불러오기
import numpy as np
a = np.array([1, 2, 3, 4, 5])
# 특정 함수만 불러오기
from numpy import array
a = array([1, 2, 3, 4, 5])

다음과 같은 방법으로 numpy 라이브러리에 내장되어있는 array함수를 사용할 수 있다.

 

numpy는 기본적으로 2차원의 데이터를 가지고 있다.

이를 활용하기 위해서는 다음과 같은 기본 지식을 가져야 된다.

 

주요 용어

  • axis: 배열의 각 축
  • rank: 축의 개수
  • shape: 축의 길이, 배열의 크기

 

예시) 3X4 형태인 배열

  • axis 0 과 axis 1 을 갖는 2차원 배열
  • rank 2 array
  • shape는 (3, 4)

 

1차원, 2차원, 3차원 리스트를 생성해주고 이를 np.array를 활용하여 배열로 변환해 줄거다.

리스트를 생성할 때 한줄로 데이터를 입력해도 되지만, 가독성을 위해 구분해 주었다.

import numpy as np

# 1차원 리스트
a1 = [1, 2, 3, 4, 5]

# 2차원 리스트
a2 = [[1.5, 2.5, 3.2],
      [4.2, 5.7, 6.4]]
      
# 3차원 리스트
a3 = [[[1, 3, 1],
       [4, 7, 6],
       [8, 3, 4]],
      [[6, 2, 4],
       [8, 1, 5],
       [5, 15, 4]]]
       
 # 리스트를 배열로 변경
 b1 = np.array(a1)
 b2 = np.array(a2)
 b3 = np.array(a3)
  • 1차원 배열
    • Rank :1
    • shape : (5, ) / (x, )
  • 2차원 배열
    • Rank :2
    • shape : (3, 5) / (x, y) 행과 열
  • 3차원 배열
    • Rank :3
    • shape : (2, 3, 5) / (x, y, z) Rank 3 부터는 행과 열이 (3, 5)인 배열이 2개 있다는 의미로 읽으면 된다.
  • 앞에서 부터 axis 0, axis 1, axis 2의 크기를 의미한다. shape의 맨 앞 aixs 0은 데이터의 건수를 의미한다.
배열명.ndim 배열 차원 확인
배열명.shape 배열의 형태(크기)를 튜플 자료형으로 제공
배열명.reshape(행, 열) 포함된 요소가 사라지지 않는 형태라면 입력한 행, 열 값으로 변환
1차원 변환시 (행, ) 또는 (행) 형태로 사용
(행, -1), (-1, 열) 표현의 경우 컴퓨터가 알아서 데이터 변환(-1은 컴퓨터에게 맡긴다는 뜻)

 

배열 인덱싱과 슬라이싱

배열[ 행, 열 ] 특정 위치의 요소를 조회  a[0, 1]
배열[행1, 행2, ... , :  ] 다양한 행 범위 조회, 전체 열 조회  a[ [ 0, 1 ], : ] 또는 
a[ [ 0, 1 ] ]  / 열은 생략 가능
배열[  :  , 열1, 열2, ... ] 전체 행 조회, 다양한 열 범위 조회  a[  : , [ 0, 1 ] ] / 행은 생략 불가
( : = 처음부터 끝까지 )
배열[  행1, 행2, ... , 열1, 열2, ...  ] 다양한 행, 열 요소로 범위 검색 a[ 0 : 2, 1 : 2 ]

 

<슬라이싱>

  • 배열[행1:행N,열1:열N] 형태로 지정해 그 위치의 요소를 조회한다.
  • 조회 결과는 2차원 배열이 된다.
  • 마지막 범위 값은 대상에 포함되지 않는다.
  • 즉, **배열[1:M, 2:N]**이라면 1 ~ M-1행, 2 ~ N-1열이 조회 대상이 된다.

<조건 조회>

  • 조건에 맞는 요소를 선택하는 방식이며, 불리안 방식이라고 부른다.
  • 조회 결과는 1차원 배열이 된다.
  • ex) 
    • score= np.array( [ [ 78, 91, 84, 89, 93, 65 ], [ 82, 87, 96, 79, 91, 73 ] ] )
    • print( score[ score >= 90 ] )  # 요소중 90이상인 것만 조회
    • print(score[ ( score >= 80 ) & ( score <= 95 ) ] ) # 요소중 80보다 크거나 같고, 95보다 작거나 같은 것만 조회

<배열 연산>

  • x = np.array([[1, 2], [3, 4]])
  • y = np.array([[5, 6], [7, 8]])
배열 더하기 x + y 또는 np.add( x, y ) 배열 빼기 x - y 또는 np.subtract( x, y )
배열 곱하기 x * y 또는 np.multiply( x, y ) 배열 나누기 x / y 또는 np.divide( x, y )
배열 지수연산 x ** y 또는 np.power( x, y )    

 

<배열 집계 / 중요!>

  • np.sum(배열명) - 전체집계
  • np.sum(배열명, axis = 0) - 열기준 집계
  • np.sum(배열명, axis = 1) - 행기준 집계

 

  • np.argmax(배열명) - 전체 중에서 가장 큰 값의 인덱스
  • np.argmax(배열명, axis = 0) - 행 방향 최대값의 인덱스
  • np.argmax(배열명, axis = 1) - 열 방향 최대값의 인덱스

 

  • np.where(배열명 조건, 조건이 참일경우 값, 거짓일 경우 값] - ex) np.where(a > 2, 1, 0)

 

 

정리 numpy

Array구조 : Axis, Rank, shape

  • 특별히 Axis 0의 의미 이해

Array 조회

  • 인덱스 : 특정 인덱스, 여러 인덱스, 범위 / 조건 조회

Array Shape 변형 : reshape

Array 연산

  • 기본연산 : 사칙연산, 지수, 제곱근

Array 집계

  • sum, mean, min, max, std … (axis = 0, 1 에 따른 연산 방향)

[Visual Studio Code]에서의 git 1의 내용까지 진행이 됬다면 이번에는 Git Graph를 설치해볼거다.

왼쪽 목록 4번째 칸에서 git graph를 검색하고 install해준다.

 

그러면 왼쪽 하단에 git graph가 생성되고 누르면 활성화 할 수 있다.(사진상으로는 우측 하단)

클랙해보면 버전의 ID, 수정된 파일, 버전의 commit매시지 작성자 등을 확인할 수 있다.

이어서 우리는 work2.txt, work3.txt 파일을 생성해주고 commit한다. 

여기서 우리의 수정사항은 work2와 work3의 텍스트 파일을 생성하고 저장한거다. 우리는 이 모든것을 commit할 수 있지만 버전관리를 위해 work2만 따로 commit하도록 아래 사진과 같이 Stage Changes기능을 통해 work2.txt파일만 commit하고 work3.txt를 v3로 commit해보겠다. 이러한 기록은 git graph를 통해 시각적으로 확인할 수 있다.

git graph를 보면 v2위에있는 파란o와 main에 있는 파란테두리가 있다. 파란 동그라미는 HAED를 가리키고 main위에있는 파란 테두리는 현재 HEAD가 보고있는 repository를 가르킨다. 이에대한 자세한 설명은 "[Git, GitHub]의 역할 원리"에서 확인하고 개념을 익히기 바란다.

 

여기서 v1으로 나의 working directory를 바꾸고 싶다면 checkout을 통해 HEAD를 바꿔준다.

그러면 우리는 v2, v3를 작업하기 이전의 v1을 commit했던 상태로 돌아가게 된다. 그 결과 work2와 work3에서 했던 작업은 없어지게 된다. 다시 돌아가고 싶다면 main을 더블클릭하거나 우클릭 후 Checkout Branch를 클릭하면 원래 working directory 버전으로 돌아가게 된다.

 

만약 detached HEAD statef를 활용하고 싶다면 v3검은 공간에서 checkout해주면 된다. 단, 기능을 잘 모를시 사용하는 것은 추천하지 않는다.

v3검은 공간에서 checkout해준다면 새로 commit한 내용들은 main과는 별개로 생성될 것이다. 

work4.txt 파일을 만들어 저장하고 test라는 commit message를 남겨 commit했다. HEAD는 test를 바라보고 main은 별개로 존재하는것을 확인할 수 있다.

 

여기서 checkout branch를 하게 된다면 commit했던 test는 사라지게 된다. 만약 이게 열심히 밤새 작업했던 결과물이라면 나는 너무나도 큰 좌절을 맞이할거다.

 

그러나 Git은 버전을 버리지 않는다. 우리가 commit id를 알고있다면 언제든지 이를 복구할 수 있다. 터미널 창을 열어 다음과 같이 입력하면 test를 불러올 수 있다.

  • git checkout f33496d2

내가 commit한 test의 id는 f33496d2임으로 여러분의 commit id는 다르기때문에 맞는 id를 입력해 주어야 된다.

만약 id를 찾지 못했다면 git reflog를 활용하여 사라진 id를 확인할 수 있다. 

 

만약 Test했던 내용들이 마음에 들어 이를 유지하고 싶을 경우에는 어떻게 해야될까?

우리는 새로운 branch를 만들에 주고 HEAD가 새로운 branch를 바라보게 한다면 문제없이 그대로 개발을 이어갈 수 있다. 

 

test 우측 검은화면에서 우클릭을 하고 Create Branch를 선택한다. 나는 test_v1이라는 이름의 branch를 생성하였고

자동으로 check out해주는 toggle을 활성화해 branch를 만들었다. 마지막 결과를 보면 HEAD는 test_v1이라는 branch를 바라보고 있는것을 확인할 수 있다. 그리고 HEAD가 main을 바라보게 check out하더라도 test는 사라지지 않는것을 확인할 수 있다.

 

main에서 새로운 commit을 시도하게 되면 새로운 가지가 생겨나게 된다.  work5.txt 파일을 만들고 v4 메시지로 commit했다. 현재 각 branch의 상태는 다음과 같다.

  • main = [ work1.txt, work2.txt, work3.txt, work5.txt ]
  • test_v1 =  [ work1.txt, work2.txt, work3.txt, work4.txt ]

test_v1에 있던 work4는 성공하여 삭제해서는 안되고 main으로 합쳐야 앞으로의 개발이 가능한 상황에서 우리가 할수 있는것은 merge를 활용하는 것이다. HEAD가 바로보는 것이 기준이기 때문에 HEAD가 main을 바라보고 있는 현 상황에서 test_v1과 merge해보겠다. test_v1에 우클릭하여 merge해주면 work4.txt파일이 main에 추가된 것을 확인할 수 있다.

 

branch를 merge해서 새로운 버전을 만들었는데 마음에 들지 않았다면 우리는 다시 되돌려야 된다.

이전에 git checkout을 통해 HEAD를 옮겼었다.

이번에는 reset을 통해서 HEAD가 바라보고있는 branch의 버전을 바꿔보자.

  • check out은 HEAD를 바꾼다.
  • reset은 HEAD가 가리키는 branch를 바꾼다.

Git에서는 git reset [commit id] 의 형태로 명령어를 작성하면 된다.

해당과정에서 2번째 사진처럼 Mixed를 Hard로 바꿔주기 바란다.

 

우리가 그동안 다른파일의 다른 내용들을 수정했다면 같은 파일에서 내용을 변경한다음 merge하게되면 어떻게 될까?

확인을 위해 main과 test_v1을 다시 merge하겠다. 이후 main에서 mergeTest.txt를 만들고 다음고 같이 데이터를 저장한다.

commit message는 start라고 하겠다.

 

start우측 빈공간에 우클릭으로 create branch를 선택하여 새로운 branch를 생성해준다.

 

이후 다음과 같은 과정을 거친다.

  • main : mergeTest.txt의 2를 m2로 변경 -> commit message "m2"입력후 commit
  • test_v2 : mergeTest.txt의 3를 t3로 변경 -> commit message "t3"입력후 commit
  • main : mergeTest.txt의 4를 m4로 변경 -> commit message "m4"입력후 commit
  • test_v2 : mergeTest.txt의 4를 t4로 변경 -> commit message "t4"입력후 commit

우측 그림과 같이 main을 선택하고 branch를 merge해준다.

 

그러면 다음과 같은 오류 메시지를 확인할 수 있다. start로 commit했을 때 생성했던 mergeTest.txt파일과 최종적으로 만들어진 파일은 각각 이러한 형태를 갖고있다.

  • start : 1 - 2 - 3 - 4
  • t4 : 1 - 2 - t3 - t4
  • m4 : 1 - m2 - 3 - m4

Git은 두 branch의 base인 start에 있던 txt파일을 확인하여 각각 수정된 부분을 적용해 합쳤다.

그렇게 1 - m2 - t3 - ?라는 결과를 얻었고 마지막 ?는 병합할 수 없어 충돌을 막기 위해 병합하지 않았다.

그리고 사용자에게 어떻게 할건지 의견을 받는 과정을 통해 문제없이 병합할 수 있게 한다.

Resolve in Merge Editor 버튼을 클릭하면 변경내용을 혹인하고 수정할 수 있다.

 

주황색 창기 사용자에게 변경을 요구하는 창이고 나는 tm4라는 값으로 이를 대체하기로 하였다.

Complete Merge버튼을 누르게 되면 안전하게 병합(merge)된다.

 

이렇게 되면 자동으로 commit message가 작성되고, 그대로 commit 해준 결과 우측 이미지와 같이 잘 병합된 것을 확인하였다.

Tip : commit message 창에서 ctrl+enter 해주게 되면 commit을 해준다.

 

이제 우리는 만들어진 프로젝트를 백업해야 된다. 이러한 백업은 원격저장소(repository)에서 이루어진다.

Git을 다루는 사람들은 대부분 GitHub를 활용해 백업한다.

GitHub에 로그인하여 New 버튼을 통해 repository를 생성 할거다.

 

Repository name에 생성할 원격저장소 이름을 적고 아래에 있는 Create repository 버튼을 눌러준다.

 

 

다음에 나오는 HTTPS Git 주소를 복사해주고

 

다음 경로를 찾아가 add remote를 선택해준다. 그러면 가운데 위에부분 창이 하나 생기는데 아까 복사했던

주소를 붙여넣은 후 Enter를 누른다.

 

아무일도 일어나지 않았다면 정상적으로 된 것이다.

빈 칸이 하나 더 생기는데 여기에는 origin이라 치고 다치 Enter를 누른다.

 

그러면 다음과 같이 main 옆에 origin이라는 글자가 뜨게된다 안전하게 GitHub와 연결이 되었다.

이후 push해주면 그 동안 commit했던 버전들이 모두 GitHub에 올라가게 된다.

 

아까 만들어 주었던 repository를 새로고침해주면 작업한 파일이 업로드 되는것을 확인할 수 있다.

 

Tip : 주소를 복사하지 못했다면 초록색 버튼을 클랙해서 주소를 확인할 수 있다.

 

'<프로그램> > Git, GitHub' 카테고리의 다른 글

[Visual Studio Code]에서의 git 3  (0) 2024.09.15
[Visual Studio Code]에서의 git 1  (0) 2024.09.05
[Git, GitHub]의 역할 원리  (5) 2024.09.05

Visual Studio Code에서 Git을 활용하는 방법에 대해 얘기해보자.

 

VS code를 실행하고 왼쪽 목록 첫번째 아이콘에서 폴더를 생성해준다.

 

Open Folder를 선택하여 원하는 곳에 폴더를 생성 및 선택하고 꼭! 그 폴더안에 들어가 지정해준다. 예를들어 바탕화면에 폴더를 만들었지만 선택하지 않고 바탕화면이 선택되었다면 우리가 Git을 연동했을 때 C:\Users\User\Desktop 경로에 있는 모든 파일들이 Git으로 관리되는 참사를 볼 수 있다.

 

이후 창에서 마우스 우클릭을 통해 파일을 생성해주자 나는 work1.txt 파일을 생성하였다.

이후 내용을 편하게 적은 후 ctrl+s로 저장을 해주어야 된다. 저장이 잘 되었는지 확인하는 방법은 파일 우측 ○가 없어졌다면 잘 저장된 것이다.

 

이후 왼쪽 목록에서 3번째 아이콘을 클릭하면 Initialize Repository라는 버튼이 있다 이를 클릭하게 되면 이 파일은 Git에서 버전 관리하게 된다. 이후 Commit을 누르게 되면 내가 작업한 변경사항을 새로운 버전으로 제출하겠다는 의미로 Git에 버전 관리하게 된다.

 

만약 이러한 애러 매시지가 뜬다면 Cancel을 누르고 터미널을 활용하여 user.name과 user.email을 작성해주면 된다.

 

상단 창에서 Terminal -> New Terminal 또는 ctrl+j를 눌러주면 터미널 창을 만들 수 있다.

윈도우 초기 사용자라면 오른쪽에 powershell이라 되어있을거다 우리는 이를 Git Bash로 변경해야된다.

나의 경우 Git Bash를 Default값으로 지정해서 터미널을 열게되면 Git Bash가 열린다.

설정 방법은 Select Default Profile을 누르고 Git Bash를 선택한다.

 

이제 우리는 Git Bash에서 다음과 같이 적어주면 된다.

  • git config --global user.name "이름"
  • git config --global user.email "이메일@gmail.com"

이를 통해 유저 이름과 이메일을 등록하였다. 이제 윗 내용을 다시 시도하면 문제없이 진행될 것이다.

'<프로그램> > Git, GitHub' 카테고리의 다른 글

[Visual Studio Code]에서의 git 3  (0) 2024.09.15
[Visual Studio Code]에서의 git 2  (0) 2024.09.05
[Git, GitHub]의 역할 원리  (5) 2024.09.05

Information Manager From Hell!

 

우리는 Git을 왜 쓰게 될까?

개발자가 일을 하다보면 한번에 모든것을 만들 수 없다. 여러 파일이 생기고 이를 [버전]이라고 한다.

Linux를 만든 '리눅스 토르발즈'는 분산 버전 관리 시스템인 Git의 창시자이다. Git은 여러 버전의 파일들을 관리하기 위해 만들어졌고 토르발즈는 이러한 분산 버전 관리 시스템을 Information Manager From Hell이라 하였다.

 

그럼 왜 이러한 버전 관리 시스템이 필요할까?

A1 버전

 

처음에 A1이라는 프로그램이 있다고 하자.

우리는 이를 더욱 발전시켜나가 A5버전 까지 출시했다.

 

A5 버전

 

개발팀은 A6를 만들기 위해 노력하다. 시스템에 지장을 줄 수 있는 버그를 발견하였고 이 버그가 어디서 생긴지 찾아야되는 상황이 발생한다. 확인 결과 A3 개발과정에서 버그가 생긴것을 확인하였다.

 

A3에서 생긴 버그

버전관리를 하고 있다면 버전을 돌려보며 어디서 이러한 문제가 생겼는지 찾을 수 있다.

A5에서 A1으로 순차적인 확인을 통해 개발팀은 A3에서 버그가 생겨났다는것을 확인했을 거다.

버전관리를 하지 않았다면 우리는 버그가 난 곳이 어딘지 모든 코드를 읽으며 찾아내야 한다. 작은 프로젝트라면 충분히 가능하지만 대규모 프로젝트의 경우 너무나도 많은 시간을 투자하게 된다.

2000만줄의 코드를 읽으며 버그를 찾는것과 A3버전 수정된 부분에서 버그를 찾는 것은 너무도 큰 차이가 난다.

이러한 버전관리는 효율적인 개발에 필수 요소라고 할 수 있다.

 

우리 Git에서 관리하는 project folder에서는 다음과 같은 절차를 통해 버전관리가 이루어진다.

working directory에서 작업한 것을 우리는 add하게 되는데 git add [파일명, 확장자명] / git add . 을 활용하여 stage area로 보내게 된다. stage area는 index, cache라고도 불린다.

여기서 commit하게되면 우리는 했던 작업을 .git 즉 repository로 올리게되며 버전관리를 하게 된다.

이렇게 repository로 올라가면서 고유의 id가 생성되는데 commit message, 파일의 이름, 파일의 내용, 부모의 commit id(존재할 시), 유저 이메일, 유저 이름을 해쉬알고리즘을 통해 유일무이한 id를 만들게 된다. 이를 고유한 식별자 commit id라고 한다. 이 id는 main에 저장된다. main은 마지막 작업자를 기록한다. 원초적으로 다가가면 마지막 버전은 HEAD가 가리키는 곳에 저장된다. 많은 repository중 HEAD가 main을 가르키고 있어 마지막 작업(버전)이 main에 기록되는 것이다.

 

처음 commit 한건이 A이고 두 번째 커밋한 것이 A2라고 한다면 A는 A2의 parent이다. HEAD는 main을 가리키고 마지막 버전 A2의 commit id는 main에 저장될 것이다. 이후 또 commit해 A3를 만들게 된다면 main에는 A3의 commit id가 나타날 것이다.

 

여기서 만약 checkout을 통해 HEAD가 가리키는 곳을 바꾸게 된다면 우리는 이전에 했던 작업으로 돌아갈 수 있다.

HEAD는 working driectory가 어느 버전과 같은지를 나타낸다. 아래 처럼 HEAD가 A를 바라보게 하면 그 동안 작업했던 A2와 A3는 사라지게 된다. 다시 working directory의 버전을 돌리고 싶으면 HEAD가 main을 보도록 checkout 또는,  checkout branch 해주면 된다.

 

여기서 만약 우리가 A3로 checkout 하게된다면 HEAD는 A3를 바라보게 될거고 여기서 commit을 하게 된다면 해로운 A4가 생기게 된다. A4는 main과는 상관없이 자기의 길을 가게 된다. 가장 무서운 점은  HEAD가 main을 바라보는 순간 A4는 사라지게 된다.

 

HEAD가 A3를 바라보고 작업한 뒤 commit하는 순간 A4는 사라질 수 있는 작업이 되는거다.

내가 5시간의 작업을 통해 만든 결과물이 한순간에 사라진다면 얼마나 슬플까?

하지만 Git은 모든것을 기억한 우리가 보기에는 없어졌지만 commit id만 알고있다면 우리는 chackout을 통해 다시 A4를 확인할 수 있다. 

 

이러한 작용을 모르고 쓰면 내가 한 모든 작업을 날리는 사고지만 알고쓴다면 기능이다.

무언가를 테스트해봐야 될 때 이러한 기능을 쓸 수 있다. 이것을 detached HEAD state라고 한다. 실험이 실패했을 때 우리는 git checkout main을 통해 아무런 문제없이 모든 상황을 되돌려 놓을 수 있다. 개발자들은 이렇게 리스크없이 실험을 할 수 있다. 

그런데 만약 실험한 기능이 정상적으로 작동되고 계획했던 모든것이 이루어 졌다면, 저A4버전을 포기할 수 없다.

이런경우 새로운 branch(repository)를 생성해주면 A4버전을 유지할 수 있다.

 

다음과 같은 상황에서 HEAD가 test라는 이름을 가진 branch(repository)를 바라보게 한다면 원래 Git의 흐름대로 개발을 이어나갈 수 있다. 그리고 main으로 checkout해도 A4는 사라지지 않는다.

작업자는 HEAD를 main을 보게 하고 새로운 버전 A5를 만들었다고 하자 그러면 우리는 2가지의 갈래길을 보게된다.

이러한 구조가 이거지면 마치 나뭇가지모양처럼 발전하게 되는데 이러한 기능을 하는 것을 나뭇가지의 영문을 사용해 'branch'라고 부른다.

 

A3에 main.txt가 있었고 A4는 test.txt를 추가로 만든 버전이라고 하자.

A5는 work.txt를 추가로 만든 버전이라면 우리는 A5에 test.txt라는 파일을 가져와야 버전이 업그레이드 될 것이다.

이러한 작업을 Merge라고 한다. A5작업에 A4를 merge하게 된다면 A5를 기준으로 없던 작업들이 추가될 것이다.

A6의 perant는 A5와 A4가 된다. 

 

만약 다른파일이 아닌 같은 파일인 경우에는 어떻게 될까?

다시 A3의 시점으로 돌아가보자 A3에서 main.txt파일에 1, 2, 3, 4라는 값이 있다면 A3에서 한 번은 3을 C3로 변경하였고

한 번은 2를 B2로 작업했다고 하자. 그 결과 각각 B버전과 C버전이 만들어진 상태다.

여기서 각자 한 번의 작업을 진행해 4를 B4와 C4로 수정하였다고 한다면, 이 두 작업을 merge할 수 있을까?

결론은 가능하다.

만약 앞에가 어떤상황인지 모른다면 어떻게 될까?

1의 내용은 양쪽이 동일해 그대로 1로 들어갈거고 나머지 2, 3, 4는 어느쪽에서 수정이 이루어졌는지 알수없다.

이 경우 수정이 되지 않은 1만 받아들이고 나머지는 합칠 수 없다. 다시 말하면 '충돌'이 일어났다.

이것을 [ Conflict ]라고 한다.

알고쓰면 기술이고 모르면 사고라고 한다. 충돌 즉 conflict가 나는 이유를 알면 Git에게 고마워해야 된다.

반대로 이유를 모른다면 우리는 Git이 원망스러울 수 밖에 없다.

 

 

Git을 만든 사람들은 여기서 획기적인 생각을 한다. 바로 origin을 찾아가서 확인을 하고 3자대면을 하게 된다. 

즉 B2와 C2가 갖고있는 main.txt의 조상을 찾아가 비교대조하는데 그 조상을 'base'라고 한다.

 

 

base를 기준으로 봤을 때 변경사항은 다음고 같다.

  • 1은 수정없이 그대로 있었다.
  • 2는 B2로 수정되었다.
  • 3은 C3로 수정되었다.
  • 4는 B4와 C4로 수정되었다.

한 쪽만 수정된 정보는 수정사항을 그대로 가져오지만 양쪽 모두 수정된 정보는 컴퓨터가 임의로 처리할 수 없어

사람에게 수정을 맡기게 된다. 컴퓨터는 병합하면 안되는 것을 병하지 않음으로 충돌을 방지한다. 이는 최고의 기능이다.

최종적으로 이러한 형태를 갖게되며 ?안에 들어갈 것은 사용자가 정의해 주면 된다.

그러면 문제없이 merge할 수 있다.

 

내가 B2와 C2를 병합해 버전 D를 만들어 냈는데 뭔가 마음에 들지 않는 부분이 생겨서 다시 돌이켜야 된다면 어떻게 해야될까? 우리는 이전에 HEAD가 바라보는 것을 바꾸기 위해 check out을 사용했다. 내가 보고있는 branch를 바꾸기 위해서는 reset을 사용해야된다.

 

  • check out은 HEAD를 바꾼다.
  • reset은 HEAD가 가리키는 branch를 바꾼다.

reset을 통해 HEAD가 바라보고 있는 main이 B2를 바라보게 만들면 Git에서 D는 보이지 않고 다시 분리된 상태로 돌아가게 된다. 우리가 보는 화면에서 D는 표시되지 않지만 commit id만 알고있다면 git checkout [commit id] 명령어를 통해 복구할 수 있다. 이를 활용해 우리는 작업을 언제든지 되돌릴 수 있고 다양한 실험을 할 수 있다.

추가적으로 test라는 이름을 가진 branch도 A, B, B2, C어디든 이동할 수 있다.

  • ex) git checkout fd86se9
  • ex) git reset fd86se9

fd86se9는 commit id의 예시이다.

 

이제 우리가 만든 프로젝트를 백업하기 위해 우리는 웹호스팅을 활용하는데 이를 GitHub통해 한다.

GitHub의 원격저장소(repository)를 이용해 프로젝트를 업로드하면 로컬저장소에서 관리하던 것을 원격저장소에서

언제든지, 인터넷이 된다면 어디에서든지 가져와 작업을 할 수 있다.

'<프로그램> > Git, GitHub' 카테고리의 다른 글

[Visual Studio Code]에서의 git 3  (0) 2024.09.15
[Visual Studio Code]에서의 git 2  (0) 2024.09.05
[Visual Studio Code]에서의 git 1  (0) 2024.09.05

+ Recent posts