project-LinearRegression

Linear Regression project

  • 한달동안 팀원들과 함께 한 프로젝트
  • 홍성현님, 유호원님, 배준영님 한달동안 수고 많았습니다 :)
  • 깃헙에서도 확인할 수 있습니다.

Linear Regression project UsedCar price

선형회귀분석에 사용한 코드| 정리

  1. 패키지 모듈

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import re
    import time
    import pandas as pd
    import seaborn as sns
    import missingno as msno
    import matplotlib as mpl
    import matplotlib.pylab as plt
    import statsmodels.api as sm
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import r2_score
    from scipy import stats
    from sklearn import linear_model
    from sklearn.metrics import mean_squared_error
    from sklearn.model_selection import cross_val_score
    from sklearn.model_selection import KFold
    # 컬럼 display 제한 상향
    pd.set_option('display.max_columns', -1)
    # 지수 표기법 해제
    pd.options.display.float_format = '{:.7f}'.format
  2. 로우데이터 임포트

    1
    raw_data = pd.read_csv("./csv_file/vehicles.csv") # ./ : 현재경로
  3. 결측치 확인 및 제거

    1
    2
    3
    import missingno as msno
    msno.matrix(start_df)
    plt.show()
  4. 결측치 수치화

    1
    2
    3
    4
    5
    6
    def columns_na_percentage(df,columns):
    for column in columns:
    percentage = round(100 -(len(df['{}'.format(column)].dropna()) / len(df)) * 100,2)
    if percentage :
    print("{} : ".format(column), percentage, "%", end='\n')
    columns_na_percentage(start_df, start_df.columns)
  5. 결측치 제거

    1
    2
    start_df = start_df.dropna(axis=0)
    print("결측치 제거후 Data : ", start_df.shape)
  6. price 컬럼 내용 확인

    1
    raw_data['price'].describe()
    1
    raw_data['price'].sort_values(ascending=False)[:10]
  7. 중복 데이터 제거

    1
    2
    start_df = start_df.loc[start_df['vin'].drop_duplicates(keep='last').index]
    print("중복 vin 삭제 Data : ", start_df.shape)
  8. VIN(차대번호) 크롤링 후 데이터 합치기

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    vin_crawling_data = pd.read_csv('./csv_file/final_vin_crawling.csv')
    # secend_half_crawling_data = pd.read_csv('./vin_crawling_addtional.csv')
    # vin_crawling_data = pd.concat([first_half_crawling_data, secend_half_crawling_data], axis=0)

    # 대문자 변경
    vin_crawling_data['vin'] = vin_crawling_data['vin'].str.upper()

    # 중복제거
    vin_crawling_data = vin_crawling_data.drop_duplicates('vin', keep='first')

    # 대문자 변경
    start_df['vin'] = start_df['vin'].str.upper()

    # merge
    merged_df = pd.merge(start_df, vin_crawling_data, on='vin')

    # 중복 제거
    merged_df = merged_df.drop_duplicates('vin', keep='first')

    # 필요 없는 컬럼 제거
    df = merged_df[merged_df.columns.difference(['id_y', 'id_x', 'Unnamed: 0', 'Unnamed: 0.1', 'og_vin'])]
    print("크롤링 Data와 중복 vin 제거 data가 합쳐진 Data : ", df.shape)
  9. 에러 컬럼 삭제

    1
    df['error'].value_counts()
    1
    2
    3
    4
    df=df[df.columns.difference(['error'])]
    df = df.dropna(axis=0)
    msno.matrix(df)
    plt.show()
  10. 데이터 필터링

    1
    2
    3
    4
    5
    6
    7
    def get_sigma_data_by_price(num):
    # price가 0인것들 제거
    sigma = df[(df['price'] >= (df['average'] - (df['stdev']*num))) & (df['price'] <= (df['average'] + (df['stdev']*num)))]
    return sigma

    sigma_2_df = get_sigma_data_by_price(2)
    print("2 sigma Data:", sigma_2_df.shape)
  11. year 항목 int로 형변환

    1
    sigma_2_df['year'] = sigma_2_df['year'].astype('int')
  12. 주행거리 아웃라이어 탐색

    1
    2
    print("주행거리가 잘못 입력된 차량 대수 : ",len(sigma_2_df[sigma_2_df['mileage'] != sigma_2_df['odometer']]))
    sigma_2_df[sigma_2_df['mileage'] != sigma_2_df['odometer']].sort_values(ascending=False,by='odometer').head(2)
    1
    2
    # 주행거리 아웃라이어 제거
    sigma_2_df = sigma_2_df[sigma_2_df['mileage'] == sigma_2_df['odometer']]
  13. 시각화: 박스플롯, 스캐터 플롯

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    plt.figure(figsize=(16,15))
    plt.subplot(311)
    sns.boxplot(x="year", y="price", data = sigma_2_df)
    plt.xticks(rotation=90)
    plt.title('연도별 가격 분포')
    plt.subplot(312)
    sns.scatterplot(sigma_2_df['year'],sigma_2_df['odometer'])
    plt.title("연도별 주행거리 분포")
    plt.tight_layout()
    plt.show()
  14. 눈으로 확인 후 수정이 필요한 데이터

    1
    2
    # year 수정해 줘야 하는 데이터
    sigma_2_df[sigma_2_df['year'] < 1980]
    1
    sigma_2_df['year'] = sigma_2_df['year'].apply(lambda x : 2019 if x < 1980 else x)
  15. 주행거리가 높은 차량 확인

    1
    sigma_2_df[sigma_2_df['odometer'] > 500000]
    1
    2
    # 일 평균 주행거리 계산하여 확인
    print("평균 주행거리 : ", round(533000.00 / (365 * 18), 2), "마일/day")
  16. 높은 가격의 차량 확인

    1
    sigma_2_df[sigma_2_df['price'] > 75000].sort_values(by=['price'],ascending=False).head()
    1
    2
    # 낮은 금액의 차량 확인
    sigma_2_df[sigma_2_df['price'] < 500].sort_values(by=['price'],ascending=False)
    1
    2
    # 이상치 가격을 보이는 차량 제거
    sigma_2_df = sigma_2_df[(sigma_2_df['vin'] != '1GCWGFBA7C1155304') & (sigma_2_df['vin'] != '1GCWGFBA8C1126880') & (sigma_2_df['vin'] != '1FMCU03115KA47874')]
  17. 주행거리 0인 데이터 확인 및 제거

    1
    2
    3
    print("판매자가 주행거리를 0으로 올려둔 경우 : ",len(sigma_2_df[sigma_2_df['odometer'] == 0]),"건")
    sigma_2_df[sigma_2_df['odometer'] == 0].head(2)
    sigma_2_df= sigma_2_df[sigma_2_df['odometer'] != 0]
  18. 분석에 필요한 컬럼

    1
    2
    3
    4
    df = sigma_2_df[['price','year','odometer','drive','fuel','manufacturer','state','title_status','transmission','type','paint_color','cylinders']]
    df = df.reset_index(drop=True)
    msno.matrix(df)
    plt.show()
  19. 종속변수 : price

    1
    df.price.sort_values(ascending=False)[:20]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    plt.figure(figsize=(17,6))
    plt.subplot(131)
    sns.distplot(np.log(df['price']))
    plt.subplot(132)
    sns.boxplot(np.log(df['price']))
    plt.subplot(133)
    stats.probplot(np.log(df['price']), plot=plt)
    plt.tight_layout()
    plt.show()
  20. 독립변수 확인

    1
    2
    3
    4
    5
    plt.figure(figsize=(16,8))
    sns.countplot(df['year'])
    plt.title("차량 제조연도")
    plt.xticks(rotation=90)
    plt.show()
  21. 주행거리 컬럼 확인

    1
    2
    3
    4
    5
    6
    7
    8
    plt.figure(figsize=(16,7))
    plt.subplot(121)
    sns.distplot(df['odometer'])
    plt.subplot(122)
    sns.distplot(df['odometer'])
    plt.tight_layout()
    plt.xlim(0,250000)
    plt.show()
  22. 카테고리 독립변수 확인

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    plt.figure(figsize=(15,20))
    plt.subplot(621)
    sns.countplot(
    data = df,
    y = "fuel",
    palette='Set1',
    )
    plt.title("연료 종류 - fuel")

    plt.subplot(622)
    sns.countplot(
    data = df,
    y = "cylinders",
    palette='Set1',
    order = df['cylinders'].value_counts().index)
    plt.title("실린더 갯수 - cylinders")

    plt.subplot(623)
    sns.countplot(
    data = df,
    y = "transmission",
    palette='Set1'
    )
    plt.title("변속기 - transmission")

    plt.subplot(624)
    sns.countplot(
    data = df,
    y = "drive",
    palette='Set1'
    )
    plt.title("구동 방식 - drive")

    plt.subplot(625)
    sns.countplot(
    data = df,
    y = "title_status",
    palette='Set1'
    )
    plt.title("차량 상태 - title_status")

    plt.tight_layout()
    plt.show()
    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
    30
    31
    32
    33
    34
    35
    36
    37
    plt.figure(figsize=(18,10))

    plt.subplot(221)
    sns.countplot(
    data = df,
    y = "type",
    palette='Set1',
    order = df['type'].value_counts().index
    )
    plt.title("차량 종류 - type")

    plt.subplot(222)
    sns.countplot(
    data = df,
    y = "paint_color",
    palette='Set1',
    order = df['paint_color'].value_counts().index)
    plt.title("차량 색깔 - paint_color")

    plt.subplot(223)
    sns.countplot(
    data = df,
    y = "manufacturer",
    palette='Set1',
    order = df['manufacturer'].value_counts().iloc[:7].index,)
    plt.title("제조사 - manufacturer")

    plt.subplot(224)
    sns.countplot(
    data = df,
    y = "state",
    palette='Set1',
    order = df['state'].value_counts().iloc[:7].index
    )
    plt.title("주 - state")
    plt.tight_layout()
    plt.show()
  23. 실수형 변수 상관분석

    1
    2
    sns.pairplot(df[['price','odometer','year']],size=3)
    plt.show()
  24. Partial Regression Plot(부분회귀분석)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    fig = plt.figure(figsize=(20,8))
    ax1 = fig.add_subplot(1,2,1)
    ax2 = fig.add_subplot(1,2,2)

    others = list(set(df.columns).difference(set(["price", "odometer"])))
    p, resids = sm.graphics.plot_partregress(
    "price", "odometer", others, data=df, obs_labels=False, ret_coords=True, ax = ax1
    )

    others2 = list(set(df.columns).difference(set(["price", "year"])))
    p, resids = sm.graphics.plot_partregress(
    "price", "year", others2, data=df, obs_labels=False, ret_coords=True, ax = ax2
    )

    plt.tight_layout()
    plt.show()
  25. 모델링, 데이터 분할

    1
    2
    3
    4
    5
    6
    7
    8
    # 빈도수에 따른 필터링
    def value_counts_filter(num, columns):
    for column in columns:
    result = df[column].value_counts()[df[column].value_counts().sort_values() < num]
    if len(result) !=0:
    print(result.values.sum())
    print(result, end="\n\n===========\n\n")
    value_counts_filter(10, df.columns.difference(['price', 'odometer', 'year']))
  26. 2개 초과 10개 미만 데이터 인덱스 확인

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def check_under_10_index(start_num, end_num, columns, df):
    test = {}
    test2 = []
    for column in columns:
    len_under_10 = len(df[column].value_counts()[(df[column].value_counts() < end_num) & (df[column].value_counts() > start_num)])
    if len_under_10:
    for i in range(len_under_10):
    index = df[df[column] == df[column].value_counts()[(df[column].value_counts() < end_num) & (df[column].value_counts() > start_num)].index[i]].index.values
    value = df[column].value_counts()[(df[column].value_counts() < end_num) & (df[column].value_counts() > start_num)].index[i]
    test[value] = index
    test2.append(test)
    return test2
    1
    2
    index_df = pd.DataFrame(check_under_10_index(2,10, ['cylinders', 'manufacturer', 'title_status', 'type'], df))
    index_df
  27. 데이터 분할에 사용할 인덱스 분류

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    for_test_data = []
    for_train_data_train = []
    for_train_data_test = []
    for column in index_df.columns:
    start = list(index_df[column][0])
    random.shuffle(start)
    if len(start) > 4:
    m = [start[i:i + 3] for i in range(0, len(start), 3)]
    for_test_data.append(m[0])
    for_train_data_train.append(m[1])
    for_train_data_test.append(m[2])
    elif len(start) == 4:
    m = [start[:2], start[2:3], start[3:]]
    for_test_data.append(m[0])
    for_train_data_train.append(m[1])
    for_train_data_test.append(m[2])
    else :
    m = [[i] for i in start]
    for_test_data.append(m[0])
    for_train_data_train.append(m[1])
    for_train_data_test.append(m[2])
  28. 10개 미만 데이터 우선 삭제

    1
    2
    3
    4
    5
    6
    7
    def delete_under_ten(df):
    for column in df.columns.difference(['id', 'price', 'odometer', 'year']):
    values = [value for value in df[column].value_counts()[df[column].value_counts() < 10].keys()]
    if values:
    for value in values:
    df = df[df[column] != value]
    return df
    1
    df_deleted_under_ten = delete_under_ten(df)
  29. 2개 초과 10개 미만 데이터 균등 분배

    1
    2
    3
    4
    5
    6
    7
    train_data, test_data = train_test_split(df_deleted_under_ten, test_size = .20, random_state = 7)
    train_data = pd.concat([train_data, df.iloc[
    [element for array in for_train_data_train for element in array] + [element for array in for_train_data_test for element in array]
    ]], axis=0)
    test_data = pd.concat([test_data,df.iloc[
    [element for array in for_test_data for element in array]]])
    train_data.shape, test_data.shape
  30. 데이터 확인

    1
    2
    msno.matrix(train_data)
    plt.show()
  31. 선형회귀분석(LinearRegression)

    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
    X = train_data[train_data.columns.difference(['id_x', 'model', 'vin', 'price'])]
    Y = np.log(train_data['price'])
    X = pd.get_dummies(data=X, drop_first=True)

    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = .20, random_state = 0)

    test_df = pd.concat([Y_train, X_train], axis=1)

    model = linear_model.LinearRegression()
    result = model.fit(X_train, Y_train)
    predicted = result.predict(X_test)
    r2 = r2_score(Y_test,predicted)
    print('Test R2 score : ', r2)
    plt.scatter(Y_test,predicted)

    pred_tr = result.predict(X_train)
    pred_test = result.predict(X_test)
    rmse_tr = (np.sqrt(mean_squared_error(Y_train, pred_tr)))
    rmse_test = (np.sqrt((Y_test, pred_test)))

    # cv = KFold(10, shuffle=True, random_state=0)
    model_0_cross_val_score = cross_val_score(model, X, Y, scoring=None, cv=10)

    print('RMSE of Train Data : ', rmse_tr)
    print('RMSE of Test Data : ', rmse_test)
    print('K-fold : ', model_0_cross_val_score)
  32. Model 1

    1
    2
    3
    from used_car_regression import UsedCarRegression
    usedcar = UsedCarRegression(df)
    df.shape
    1
    2
    # 모델1 full rank
    model1_formula = "np.log(price) ~ scale(odometer) + scale(year) +C(manufacturer)+C(cylinders)+C(drive)+C(fuel)+C(state)+C(title_status)+C(transmission)+C(type)+C(paint_color) + 0"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def summary_minimize_df(result, model_num):
    df = pd.DataFrame([{
    "R-squared": result.rsquared,
    "Adj. R-squared": result.rsquared_adj,
    "F-statistic": result.fvalue,
    "Prob (F-statistic)": result.f_pvalue,
    "Log-Likelihood": result.llf,
    "AIC": result.aic,
    "BIC": result.bic
    }]).T
    df.columns = [model_num]
    return df
  33. 모델 학습

    1
    2
    result, train_data, test_data, train_ls, test_ls = usedcar.model_fit(model1_formula)
    pred = result.predict(test_data)
    1
    2
    model1_min_df = summary_minimize_df(result, "Model 1")
    model1_min_df
    1
    2
    # R2 score 확인
    r2_score(np.log(test_data['price']), pred)
  34. 교차 검증(cross validation)

    1
    2
    cross_validation_model1 = usedcar.cross_validation(model1_formula)
    cross_validation_model1
  35. ANOVA 독립변수 확인

    1
    2
    anova = sm.stats.anova_lm(result, typ=2)
    anova.sort_values(by=['F'], ascending=False)
  36. Model 2 : 실수형 * 범주형 interaction

    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
    Numbers = ['scale(odometer)', 'scale(year)']
    X = ['fuel', 'title_status', 'transmission', 'drive', 'type', 'paint_color',"cylinders", "manufacturer"]
    combination_score = []
    combination_name = []
    for number in Numbers:
    feature = number

    for i in X:
    interaction = feature + ":" + i

    kf = KFold(5, shuffle=True, random_state=0)
    mode2_cross_val_score = []
    for X_train_index, X_test_index in kf.split(train_data):

    X_train= train_data.iloc[X_train_index]
    X_test = train_data.iloc[X_test_index]

    X_train = pd.concat([X_train, train_data[train_data.index.isin([element for array in for_train_data_train for element in array])]], axis=0)
    X_test = pd.concat([X_test, train_data[train_data.index.isin([element for array in for_train_data_test for element in array])]], axis=0)
    model1 = sm.OLS.from_formula("np.log(price) ~ scale(odometer) + scale(year) +{}+{}".format("+".join(X_train.columns.difference(["price",'odometer','year'])), interaction), X_train)
    result = model1.fit()
    pred = result.predict(X_test)
    R2 = r2_score(np.log(X_test.price), pred)
    n = train_data.shape[0]
    p = len(pd.get_dummies(train_data).columns)
    Adjr2 = 1-(1-R2)*(n-1)/(n-p-1)
    mode2_cross_val_score.append(Adjr2)
    combination_score.append(mode2_cross_val_score)
    combination_name.append(interaction)
    1
    2
    3
    4
    fig1, ax1 = plt.subplots(figsize=(14, 6))
    ax1.boxplot(combination_score)
    ax1.set_xticklabels(combination_name, rotation=90)
    plt.show()
  37. Model 2 formula

    1
    2
    model2_formula = "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color)\
    + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel)"
  38. 모델학습

    1
    2
    3
    4
    result, train_data, test_data, train_ls, test_ls  = usedcar.model_fit(model2_formula)
    pred = result.predict(test_data)
    model2_min_df = summary_minimize_df(result, 'Model 2')
    pd.concat([model1_min_df,model2_min_df], axis=1)
    1
    2
    # R2 score
    r2_score(np.log(test_data['price']), pred)
  39. CV

    1
    2
    cross_validation_model2 = usedcar.cross_validation(model2_formula)
    cross_validation_model2
  40. Model 3 : 범주형 * 범주형 interaction

    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
    X = ['fuel', 'title_status', 'transmission', 'drive', 'type', 'paint_color', 'cylinders',  'manufacturer']
    combination_score = []
    combination_name = []
    for count in range(len(X)):
    feature = X[-1]
    X.pop()

    for i in X:
    interaction = feature + ":" + i

    kf = KFold(5, shuffle=True, random_state=0)
    model_cross_val_score = []
    for X_train_index, X_test_index in kf.split(train_data):

    X_train= train_data.iloc[X_train_index]
    X_test = train_data.iloc[X_test_index]

    X_train = pd.concat([X_train, train_data[train_data.index.isin([element for array in for_train_data_train for element in array])]], axis=0)
    X_test = pd.concat([X_test, train_data[train_data.index.isin([element for array in for_train_data_test for element in array])]], axis=0)
    model1 = sm.OLS.from_formula("np.log(price) ~ scale(odometer) + scale(year) + {} + {}".format("+".join(X_train.columns.difference(['price', 'odometer', 'year', feature, i])), interaction), X_train)
    result = model1.fit()
    pred = result.predict(X_test)
    R2 = r2_score(np.log(X_test.price),pred)
    n = train_data.shape[0]
    p = len(pd.get_dummies(train_data).columns)
    Adjr2 = 1-(1-R2)*(n-1)/(n-p-1)
    model_cross_val_score.append(Adjr2)
    combination_score.append(model_cross_val_score)
    combination_name.append(interaction)
    1
    2
    3
    4
    fig1, ax1 = plt.subplots(figsize=(14, 6))
    ax1.boxplot(combination_score)
    ax1.set_xticklabels(combination_name, rotation=90)
    plt.show()
    1
    model3_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive)"
  41. 모델학습

    1
    2
    3
    4
    result, train_data, test_data, train_ls, test_ls = usedcar.model_fit(model3_formula)
    pred = result.predict(test_data)
    model3_min_df = summary_minimize_df(result, 'Model 3')
    pd.concat([model1_min_df, model2_min_df, model3_min_df], axis=1)
    1
    2
    # R2 score
    r2_score(np.log(test_data['price']), pred)
  42. CV

    1
    2
    cross_validation_model3 = usedcar.cross_validation(model3_formula)
    cross_validation_model3
  43. Model 4 : year와 odometer 다항식 추가

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # Partial Regression Plot을 확인하면 약간 2차방정식 모형처럼 보여서 진행
    fig = plt.figure(figsize=(20,8))
    ax1 = fig.add_subplot(1,2,1)
    ax2 = fig.add_subplot(1,2,2)

    others = list(set(df.columns).difference(set(["price", "odometer"])))
    p, resids = sm.graphics.plot_partregress(
    "price", "odometer", others, data=df, obs_labels=False, ret_coords=True, ax = ax1
    )

    others2 = list(set(df.columns).difference(set(["price", "year"])))
    p, resids = sm.graphics.plot_partregress(
    "price", "year", others2, data=df, obs_labels=False, ret_coords=True, ax = ax2
    )
    plt.tight_layout()
    plt.show()
  44. year에 제곱항을 더했을 때

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    score = []

    formula = "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive)"

    for i in range(1, 5):
    if i == 1:
    score.append(usedcar.cross_validation(formula))
    else:
    formula += " + scale(I(year**{}))".format(i)
    score.append(usedcar.cross_validation(formula))
    1
    2
    3
    4
    5
    name = ['1차항', '2차항', '3차항', '4차항']
    fig1, ax1 = plt.subplots(figsize=(14, 6))
    ax1.boxplot(score[:4])
    ax1.set_xticklabels(name)
    plt.show()
  45. odometer에 제곱항을 더했을 때

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    score = []

    formula = "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color)"

    for i in range(1,5):
    if i == 1:
    score.append(usedcar.cross_validation(formula))
    else:
    formula += " + scale(I(odometer**{}))".format(i)
    score.append(usedcar.cross_validation(formula))
    1
    2
    3
    4
    5
    name = ['1차항', '2차항', '3차항', '4차항']
    fig1, ax1 = plt.subplots(figsize=(14, 6))
    ax1.boxplot(score)
    ax1.set_xticklabels(name)
    plt.show()
  46. year와 odometer 3차항까지 추가한 모델 비교

    1
    2
    3
    model4_1_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3)) + scale(I(odometer**2)) + scale(I(odometer**3)) "
    model4_2_formula = "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3)) "
    model4_3_formula = "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(odometer**2)) + scale(I(odometer**3)) "
  47. 검증결과 비교

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    cross_validation_model4_1 = usedcar.cross_validation(model4_1_formula)
    cross_validation_model4_2 = usedcar.cross_validation(model4_2_formula)
    cross_validation_model4_3 = usedcar.cross_validation(model4_3_formula)
    fig, ax = plt.subplots(figsize=(15, 8))

    ax.boxplot(
    [
    cross_validation_model4_1, cross_validation_model4_2, cross_validation_model4_3
    ],
    sym="b*",
    labels=['Model 4_1(year, odometer 다차항)', 'Model 4_2(year 다차항)', 'Model 4_3(odometer 다차항)'],
    )
    plt.title('모델별 K-fold 검증 비교')
    plt.show()
  48. Model 4 결론 : year에 3차항까지 추가

    1
    model4_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3)) "
  49. 모델학습

    1
    2
    3
    4
    result, train_data, test_data, train_ls, test_ls = usedcar.model_fit(model4_formula)
    pred = result.predict(test_data)
    model4_min_df = summary_minimize_df(result, 'Model 4')
    pd.concat([model1_min_df, model2_min_df, model3_min_df, model4_min_df], axis=1)
    1
    2
    # R2 score 확인
    r2_score(np.log(test_data['price']),pred)
  50. CV

    1
    2
    cross_validation_model4 = usedcar.cross_validation(model4_formula)
    cross_validation_model4
  51. 모델별 성능 비교

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    fig, ax = plt.subplots(figsize=(15, 8))

    ax.boxplot(
    [
    cross_validation_model1, cross_validation_model2,
    cross_validation_model3, cross_validation_model4,
    ],
    sym="b*",
    labels=['Model 1', 'Model 2', 'Model 3', 'Model 4'],
    )
    plt.title('모델별 K-fold 검증 비교')
    plt.show()
  52. Model 5 : 중고차 워런티 제도로 odometer 분포 탐색

    1
    2
    3
    4
    5
    6
    7
    8
    plt.figure(figsize=(16,7))
    plt.subplot(121)
    sns.distplot(df['odometer'])
    plt.subplot(122)
    sns.distplot(df['odometer'])
    plt.tight_layout()
    plt.xlim(0,150000)
    plt.show()
  53. 6만 마일 기준, 이하 = 0, 초과 = 1

    1
    2
    df['odometer_under_100000'] =  df['odometer'].apply(lambda x:  0 if x >= 60000 else 1)
    df['odometer_under_100000'].unique()
    1
    usedcar = UsedCarRegression(df)
    1
    model5_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3))  + C(odometer_under_100000)"
  54. 모델학습

    1
    2
    3
    4
    result, train_data, test_data, train_ls, test_ls = usedcar.model_fit(model5_formula)
    pred = result.predict(test_data)
    model5_min_df = summary_minimize_df(result, 'Model 5')
    pd.concat([model1_min_df,model2_min_df,model3_min_df,model4_min_df,model5_min_df], axis=1)
    1
    2
    # R2 score 확인
    r2_score(np.log(test_data['price']), pred)
    1
    2
    3
    # CV
    cross_validation_model5 = usedcar.cross_validation(model5_formula)
    cross_validation_model5
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 모델별 성능비교
    fig, ax = plt.subplots(figsize=(15, 8))

    ax.boxplot(
    [
    cross_validation_model1, cross_validation_model2,
    cross_validation_model3, cross_validation_model4,
    cross_validation_model5
    ],
    sym="b*",
    labels=['Model 1', 'Model 2', 'Model 3', 'Model 4', 'Model 5'],
    )
    plt.title('모델별 K-fold 검증 비교')
    plt.show()
  55. Model 6 : 성능이 가장 좋은 모델4에 Ridge, Lasso, Elastic Net 적용

주의 : 시간이 오래걸림, 기억으론 약 4시간 30분 정도 소요됨

1
2
df = df.drop('odometer_under_100000',axis=1)
df.shape
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
usedcar = UsedCarRegression(df)
model6_formula = "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3)) "
result, train_data, test_data, train_ls, test_ls = usedcar.model_fit(model6_formula)

# Lasso
from tqdm.notebook import tqdm
bar_total = tqdm(np.arange(0.00001, 0.0005, 0.00001))

lasso_ls=[]
for i in bar_total:
lasso_result = usedcar.regularized_method(model6_formula, cv=5, alpha=i ,L1_wt=1)
lasso_ls.append({
"alpha" : i,
"Lasso" : lasso_result
})

# 데이터 프레임으로 저장
lasso_df = pd.DataFrame(lasso_ls)
lasso_df.to_csv('./lasso.csv')
1
2
3
4
5
6
7
8
9
10
11
12
13
# Ridge
from tqdm.notebook import tqdm
bar_total = tqdm(np.arange(0.00001, 0.0005, 0.00001))

ridge_ls=[]
for i in bar_total:
ridge_result = usedcar.regularized_method(model6_formula, cv=5, alpha=i, L1_wt=0)
ridge_ls.append({
"alpha" : i,
"Ridge" : ridge_result
})
ridge_df = pd.DataFrame(ridge_ls)
ridge_df.to_csv('./ridge.csv')
1
2
3
4
5
6
7
8
9
10
11
# Elastic_net
from tqdm.notebook import tqdm
bar_total = tqdm(np.arange(0.00001, 0.0005, 0.00001))

elastic_net_ls=[]
for i in bar_total:
elastic_net_result = usedcar.regularized_method(model6_formula, cv=5, alpha=i, L1_wt=0.5)
elastic_net_ls.append({
"alpha" : i,
"Elastic_net" : elastic_net_result
})
1
2
3
elastic_net_df = pd.read_csv('./csv_file/elastic.csv', index_col=[0])
ridge_df = pd.read_csv('./csv_file/ridge.csv', index_col=[0])
lasso_df = pd.read_csv('./csv_file/lasso.csv', index_col=[0])
1
2
3
lasso_df['Lasso'] = lasso_df['Lasso'].str.extract(r'(0[.][0-9]*)').astype('float')
ridge_df['Ridge'] = ridge_df['Ridge'].str.extract(r'(0[.][0-9]*)').astype('float')
elastic_net_df['Elastic_net'] = elastic_net_df['Elastic_net'].str.extract(r'(0[.][0-9]*)').astype('float')
1
2
3
4
5
6
plt.figure(figsize=(16,8))
plt.plot('alpha','Lasso', data=lasso_df)
plt.plot('alpha','Ridge', data=ridge_df)
plt.plot('alpha','Elastic_net', data=elastic_net_df)
plt.legend()
plt.show()
1
model6_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3))"
1
usedcar = UsedCarRegression(df)
1
result, train_data, test_data, train_ls, test_ls = usedcar.model_fit(model6_formula)
1
2
score, result, cross_validation_model6 = usedcar.regularized_method(
model6_formula, cv=10, alpha=0.00001, L1_wt=0)
  1. K-Fold 검증 성능 비교
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # odometer 6만
    fig, ax = plt.subplots(figsize=(15, 8))
    ax.boxplot(
    [
    cross_validation_model1, cross_validation_model2,
    cross_validation_model3, cross_validation_model4,
    cross_validation_model5, cross_validation_model6
    ],
    sym="b*",
    labels=['Model 1', 'Model 2', 'Model 3', 'Model 4', 'Model 5', 'Model 6'],
    )
    plt.title('모델별 K-fold 검증 비교')
    plt.show()
  2. 모델별 Test 데이터로 예측한 R-quare
    1
    model1_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color)"
    1
    2
    3
    4
    result = usedcar.model_fit(model1_formula)
    prediction = result[0].predict(test_data[test_data.columns.difference(['price'])])
    model_1_test_r2 = r2_score(np.log(test_data['price']), prediction)
    print("Model 1 R-sqaure : ", model_1_test_r2)
    1
    model2_formula = "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer)"
    1
    2
    3
    4
    result= usedcar.model_fit(model2_formula)
    prediction = result[0].predict(test_data[test_data.columns.difference(['price'])])
    model_2_test_r2 = r2_score(np.log(test_data['price']), prediction)
    print("Model 2 R-sqaure : ", model_2_test_r2)
    1
    model3_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + C(manufacturer):C(cylinders) + C(type):C(drive)"
    1
    2
    3
    4
    result= usedcar.model_fit(model3_formula)
    prediction = result[0].predict(test_data[test_data.columns.difference(['price'])])
    model_3_test_r2 = r2_score(np.log(test_data['price']), prediction)
    print("Model 3 R-sqaure : ", model_3_test_r2)
    1
    model4_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3)) "
    1
    2
    3
    4
    5
    result = usedcar.model_fit(model4_formula)
    prediction = result[0].predict(test_data[test_data.columns.difference(
    ['price'])])
    model_4_test_r2 = r2_score(np.log(test_data['price']), prediction)
    print("Model 4 R-sqaure : ", model_4_test_r2)
    1
    model5_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type)+scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3)) + C(odometer_under_100000)"
    1
    df['odometer_under_100000'] =  df['odometer'].apply(lambda x:  0 if x >= 60000 else 1)
    1
    usedcar = UsedCarRegression(df)
    1
    test_data['odometer_under_100000'] =  test_data['odometer'].apply(lambda x:  0 if x >= 60000 else 1)
    1
    2
    3
    4
    5
    result = usedcar.model_fit(model5_formula)
    prediction = result[0].predict(test_data[test_data.columns.difference(
    ['price'])])
    model_5_test_r2 = r2_score(np.log(test_data['price']), prediction)
    print("Model 5 R-sqaure : ", model_5_test_r2)
    1
    2
    test_data = test_data.drop('odometer_under_100000', axis=1)
    df = df.drop('odometer_under_100000', axis=1)
    1
    model6_formula =  "np.log(price) ~ scale(odometer) + scale(year) + C(manufacturer) + C(cylinders) + C(drive) + C(fuel) + C(state) + C(title_status) + C(transmission) + C(type) + C(paint_color) + scale(odometer):C(cylinders) + scale(odometer):C(type) + scale(year):C(manufacturer) + scale(year):C(type) + scale(odometer):C(fuel) + C(manufacturer):C(cylinders) + C(type):C(drive) + scale(I(year**2)) + scale(I(year**3))"
    1
    result, train_data, test_data, train_ls, test_ls = usedcar.model_fit(model6_formula)
    1
    2
    score, result, cross_validation_model6 = usedcar.regularized_method(
    model6_formula, cv=10, alpha=0.00001, L1_wt=0)
    1
    2
    3
    4
    prediction = result.predict(test_data[test_data.columns.difference(
    ['price'])])
    model_6_test_r2 = r2_score(np.log(test_data['price']), prediction)
    print("Model 6 R-sqaure : ", model_6_test_r2)
    1
    2
    3
    4
    5
    6
    7
    8
    final_result = {
    "model 1": model_1_test_r2,
    "model 2": model_2_test_r2,
    "model 3": model_3_test_r2,
    "model 4": model_4_test_r2,
    "model 5": model_5_test_r2,
    "model 6": model_6_test_r2,
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    pd.DataFrame(final_result,index=[0]).plot.bar(figsize=(20,10))
    plt.ylim(0.8,0.9)
    plt.title('모델별 Test 데이터 R2 score')
    plt.tick_params(
    axis='x', # changes apply to the x-axis
    which='both', # both major and minor ticks are affected
    bottom=False, # ticks along the bottom Edge are off
    top=False, # ticks along the top Edge are off
    labelbottom=False) # labels along the bottom Edge are off
    plt.show()
  3. 가설검증 1

주행거리 5만킬로 미터 이하일 때 파는게 좋을것이다.

1
sigma_2_df['vehicle'].value_counts()[:5]
1
2
3
# 거래가 가장 많은 차종으로 선택
data = sigma_2_df[sigma_2_df['vehicle'] == "2012 Ford F-150 FX4"]
data = data[['price','year','odometer','drive','fuel','manufacturer','state','title_status','transmission','type','paint_color','cylinders']]
1
2
3
4
5
6
7
8
9
def hypothesis_proof(df,result):
# odometer 외의 컬럼은 다 최빈값으로 채운다
for column in df.columns.difference(['price','odometer']):
df[column] = df[column].value_counts().keys()[0]

df['odometer'] = range(0, len(df)*1000, 1000)
df.reset_index(drop=True)

return np.exp(result.predict(df)), df
1
hypothesis_1, data = hypothesis_proof(data, result)
1
2
3
4
plt.figure(figsize=(20,9))
plt.plot(hypothesis_1)
plt.title("2012 Ford F-150 FX4 차량의 주행거리에 따른 가격 변화")
plt.show()

가설 1 결론 : 3만 마일 (약 48000km)부터 가격이 급격하게 떨어지는걸 볼수 있다.

  1. 가설검증 2

지역별 가격차이가 있을 것이다

1
2
anova = sm.stats.anova_lm(result, typ=2)
anova.sort_values(by=['F'], ascending=False).iloc[5:]
1
2
3
4
5
6
data = sigma_2_df[sigma_2_df['vehicle'] == "2012 Ford F-150 FX4"]
plt.figure(figsize=(20,8))
data.groupby('state').mean()['price'].sort_values(ascending=False).plot.bar()
plt.xticks(rotation=0)
plt.title("2012 Ford F-150 FX4")
plt.show()
  1. 회고
    Keep
  • 분석 목적
  • 가설 설정
  • EDA 및 전처리

Problem

  • 자동차 보증수리 여부에 대한 데이터의 부재로, Model 5의 아이디어를 좀더 발전 시키지 못한 점
  • 머신러닝 때 배운 페이스북 예측 모델 넣어서 예측해보기

Try

  • 주기적으로 업데이트되는 데이터를 활용한 예측 진행

re(regular expression)

  • 정규표현식

    • regular expression
    • 특정한 패턴과 일치하는 문자열를 ‘검색’, ‘치환’, ‘제거’ 하는 기능을 지원
    • 정규표현식의 도움없이 패턴을 찾는 작업(Rule 기반)은 불완전 하거나, 작업의 cost가 높음
    • e.g) 이메일 형식 판별, 전화번호 형식 판별, 숫자로만 이루어진 문자열 등
  • raw string

    • 문자열 앞에 r이 붙으면 해당 문자열이 구성된 그대로 문자열로 변환
1
2
3
4
5
a = 'abcdef\n' # escapce 문자열
print(a)

b = r'abcdef\n'
print(b)

기본 패턴

  • a, X, 9 등등 문자 하나하나의 character들은 정확히 해당 문자와 일치

    • e.g) 패턴 test는 test 문자열과 일치
    • 대소문자의 경우 기본적으로 구별하나, 구별하지 않도록 설정 가능
  • 몇몇 문자들에 대해서는 예외가 존재하는데, 이들은 틀별한 의미로 사용 됨

    • . ^ $ * + ? { } [ ] \ | ( )
  • . (마침표) - 어떤 한개의 character와 일치 (newline(엔터) 제외)

  • \w - 문자 character와 일치 [a-zA-Z0-9_]

  • \s - 공백문자와 일치

  • \t, \n, \r - tab, newline, return

  • \d - 숫자 character와 일치 [0-9]

  • ^ = 시작, $ = 끝 각각 문자열의 시작과 끝을 의미

  • \가 붙으면 스페셜한 의미가 없어짐. 예를들어 \.는 .자체를 의미 \\는 \를 의미

  • 자세한 내용은 링크 참조 https://docs.python.org/3/library/re.html

search method

  • 첫번째로 패턴을 찾으면 match 객체를 반환
  • 패턴을 찾지 못하면 None 반환
1
2
3
4
5
6
7
8
9
10
import re

m = re.search(r'abc', '123abdef')
m

m = re.search(r'\d\d\d\w', '112abcdef119')
m

m = re.search(r'..\w\w', '@#$%ABCDabcd')
m

metacharacters (메타 캐릭터)

[] 문자들의 범위를 나타내기 위해 사용

  • [] 내부의 메타 캐릭터는 캐릭터 자체를 나타냄
  • e.g)
  • [abck] : a or b or c or k
  • [abc.^] : a or b or c or . or ^
  • [a-d] : -와 함께 사용되면 해당 문자 사이의 범위에 속하는 문자 중 하나
  • [0-9] : 모든 숫자
  • [a-z] : 모든 소문자
  • [A-Z] : 모든 대문자
  • [a-zA-Z0-9] : 모든 알파벳 문자 및 숫자
  • [^0-9] : ^가 맨 앞에 사용 되는 경우 해당 문자 패턴이 아닌 것과 매칭
1
2
3
4
5
6
7
re.search(r'[cbm]at', 'aat')

re.search(r'[0-4]haha', '7hahah')

re.search(r'[abc.^]aron', 'daron')

re.search(r'[^abc]aron', '0aron')

\

  1. 다른 문자와 함께 사용되어 특수한 의미를 지님
    • \d : 숫자를 [0-9]와 동일
    • \D : 숫자가 아닌 문자 [^0-9]와 동일
    • \s : 공백 문자(띄어쓰기, 탭, 엔터 등)
    • \S : 공백이 아닌 문자
    • \w : 알파벳대소문자, 숫자 [0-9a-zA-Z]와 동일
    • \W : non alpha-numeric 문자 [^0-9a-zA-Z]와 동일
  2. 메타 캐릭터가 캐릭터 자체를 표현하도록 할 경우 사용
    • \. , \\
1
2
3
re.search(r'\Sand', 'apple land banana')

re.search(r'\.and', '.and')

.

  • 모든 문자를 의미
1
re.search(r'p.g', 'pig')

반복패턴

  • 패턴 뒤에 위치하는 *, +, ?는 해당 패턴이 반복적으로 존재하는지 검사
    • ‘+’ -> 1번 이상의 패턴이 발생
    • ‘*’ -> 0번 이상의 패턴이 발생
    • ‘?’ -> 0 혹은 1번의 패턴이 발생
  • 반복을 패턴의 경우 greedy하게 검색 함, 즉 가능한 많은 부분이 매칭되도록 함
    • e.g) a[bcd]*b 패턴을 abcbdccb에서 검색하는 경우
      • ab, abcb, abcbdccb 전부 가능 하지만 최대한 많은 부분이 매칭된 abcbdccb가 검색된 패턴
1
2
3
4
5
6
7
8
9
10
11
re.search(r'a[bcd]*b', 'abcbdccb')

re.search(r'b\w+a', 'banana')

re.search(r'i+', 'piigiii')

re.search(r'pi+g', 'pg')

re.search(r'pi*g', 'pg')

re.search(r'https?', 'http://www.naver.com')

^, $

  • ^ 문자열의 맨 앞부터 일치하는 경우 검색
  • $ 문자열의 맨 뒤부터 일치하는 경우 검색
1
2
3
4
5
6
7
8
9
re.search(r'b\w+a', 'cabana')

re.search(r'^b\w+a', 'cabana')

re.search(r'^b\w+a', 'babana')

re.search(r'b\w+a$', 'cabana')

re.search(r'b\w+a$', 'cabanap')

grouping

  • ()을 사용하여 그루핑
  • 매칭 결과를 각 그룹별로 분리 가능
  • 패턴 명시 할 때, 각 그룹을 괄호() 안에 넣어 분리하여 사용
1
2
3
4
m = re.search(r'(\w+)@(.+)', 'test@gmail.com')
print(m.group(1))
print(m.group(2))
print(m.group(0))

{}

  • *, +, ?을 사용하여 반복적인 패턴을 찾는 것이 가능하나, 반복의 횟수 제한은 불가
  • 패턴뒤에 위치하는 중괄호{}에 숫자를 명시하면 해당 숫자 만큼의 반복인 경우에만 매칭
  • {4} - 4번 반복
  • {3,4} - 3 ~ 4번 반복
1
re.search('pi{3,5}g', 'piiiiig')

미니멈 매칭(non-greedy way)

  • 기본적으로 *, +, ?를 사용하면 greedy(맥시멈 매칭)하게 동작함
  • *?, +?을 이용하여 해당 기능을 구현
1
2
3
re.search(r'<.+>', '<html>haha</html>')

re.search(r'<.+?>', '<html>haha</html>')

{}?

  • {m,n}의 경우 m번 에서 n번 반복하나 greedy하게 동작
  • {m,n}?로 사용하면 non-greedy하게 동작. 즉, 최소 m번만 매칭하면 만족
1
2
3
re.search(r'a{3,5}', 'aaaaa')

re.search(r'a{3,5}?', 'aaaaa')

match

  • search와 유사하나, 주어진 문자열의 시작부터 비교하여 패턴이 있는지 확인
  • 시작부터 해당 패턴이 존재하지 않다면 None 반환
1
2
3
4
5
re.match(r'\d\d\d', 'my number is 123')

re.match(r'\d\d\d', '123 is my number')

re.search(r'^\d\d\d', '123 is my number')

findall

  • search가 최초로 매칭되는 패턴만 반환한다면, findall은 매칭되는 전체의 패턴을 반환
  • 매칭되는 모든 결과를 리스트 형태로 반환
1
re.findall(r'[\w-]+@[\w.]+', 'test@gmail.com haha test2@gmail.com nice test test')

sub

  • 주어진 문자열에서 일치하는 모든 패턴을 replace
  • 그 결과를 문자열로 다시 반환함
  • 두번째 인자는 특정 문자열이 될 수도 있고, 함수가 될 수 도 있음
  • count가 0인 경우는 전체를, 1이상이면 해당 숫자만큼 치환 됨
1
re.sub(r'[\w-]+@[\w.]+', 'great', 'test@gmail.com haha test2@gmail.com nice test test', count=1)

compile

  • 동일한 정규표현식을 매번 다시 쓰기 번거로움을 해결
  • compile로 해당표현식을 re.RegexObject 객체로 저장하여 사용가능
1
2
3
email_reg = re.compile(r'[\w-]+@[\w.]+')
email_reg.search('test@gmail.com haha good')
email_reg.findall()

연습문제

  • 아래 뉴스에서 이메일 주소를 추출해 보세요
  • 다음중 올바른 (http, https) 웹페이지만 찾으시오
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
30
31
32
33
34
35
import requests
from bs4 import BeautifulSoup
> 위의 두 모듈이 없는 경우에는 pip install requests bs4 실행

def get_news_content(url):
response = requests.get(url)
content = response.text

soup = BeautifulSoup(content, 'html5lib')

div = soup.find('div', attrs = {'id' : 'harmonyContainer'})

content = ''
for paragraph in div.find_all('p'):
content += paragraph.get_text()

return content

news1 = get_news_content('https://news.v.daum.net/v/20190617073049838')
print(news1)

email_reg = re.compile(r'[\w-]+@[\w.]+\w+')
email_reg.search(news1)

webs = ['http://www.test.co.kr',
'https://www.test1.com',
'http://www.test.com',
'ftp://www.test.com',
'http:://www.test.com',
'htp://www.test.com',
'http://www.google.com',
'https://www.homepage.com.']

web_reg = re.compile(r'https?://[\w.]+\w+$')
list(map(lambda w:web_reg.search(w) != None, webs))

webcrawling

web crawling 하기전에 알아둬야 할 사항

  • 예를들어, 네이버 홈페이지를 크롤링한다고 하면 www.naver.com/robots.txt을 브라우저 주소창에 입력하면 로봇 배제 규약에 관한 내용이 나옵니다.

robots.txt 내용 요약

  1. 모든 로봇 접근 허락
    User-agent: *
    Allow : /
    1. 모든 로봇 접근 차단
      User-agent: *
      Disallow: /
    2. 모든 로봇에 디렉토리 3곳 접근 차단
      User-agent: *
      Disallow: /cgi-bin/
      Disallow: /tmp/
      Disallow: /junk/
    3. 모든 로봇에 특정 파일 접근 차단
      User-agent: *
      Disallow: /directory/file.html
    4. BadBot 로봇에 모든 파일 접근 차단
      User-agent: BadBot
      Disallow: /
    5. BadBot과 Googlebot에 특정 디렉토리 접근 차단
      User-agent: BadBot
      User-agent: Googlebot
      Disallow: /private/

참고사항 2020년 4월 21일 현재

  1. 네이버 로봇 규약 설정
    출처: https://searchadvisor.naver.com/guide/seo-basic-robots
  • 사이트의 루트 페이지만 수집 허용으로 설정합니다.
    User-agent: *
    Disallow: /
    Allow: /$

  1. 다음 로봇 규약 설정
  • 모든 로봇의 접근 차단
    User-agent: *
    Disallow: /

    1. 카카오 로봇 규약 설정
  • 모든 로봇의 접근 차단

  • See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file

  • To ban all spiders from the entire site uncomment the next two lines:
    User-agent: *
    Disallow: /

문제가 있거나 오타가 있으면 댓글이나 메일로 알려주세요.
감사합니다 :)

자세한 내용은 아래 사이트를 참조하세요.
출처: https://gbsb.tistory.com/80
출처: https://medium.com/@euncho/robots-txt-e08328c4f0fd
출처: https://support.google.com/webmasters/answer/6062596?hl=ko
출처: https://ko.wikipedia.org/wiki/%EB%A1%9C%EB%B4%87_%EB%B0%B0%EC%A0%9C_%ED%91%9C%EC%A4%80

webcrawling

python의 request 모듈을 사용하여 http request/resopnse 확인하기

requests 모듈

  1. http request/response를 위한 모듈
  2. HTTP method를 메소드 명으로 사용하여 request 요청 예) get, post
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
# get 방식
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
resp.text

# post 방식
url = 'https://www.kangcom.com/member/member_check.asp'
data = {
'id': 'testid',
'pwd': 'password'
}

resp = requests.post(url, data=data)
resp.text

HTTP header 데이터 이용하기

  1. header 데이터 구성하기
  2. header 데이터 전달하기
1
2
3
4
5
6
7
url = 'https://news.v.daum.net/v/20190728165812603'
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}

resp = requests.get(url, headers=headers)
resp.text

HTTP response 처리하기

  1. response 객체의 이해
  2. status_code 확인하기
  3. text 속성 확인하기
1
2
3
4
5
6
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
if resp.status_code == 200:
resp.headers
else:
print('error')

BeautifulSoup의 find와 find_all 함수

  1. find 함수
    • 조건에 만족하는 하나의 tag만 검색
    • 특정 html tag를 검색
    • 검색 조건을 명시하여 찾고자하는 tag를 검색
1
2
3
4
5
6
7
8
tag = soup.find('h3')
tag.get_text()

tag = soup.find('p')
tag.get_text()

tag = soup.find('div', id='upper')
tag.get_text().strip()
  1. find_all함수

    • 조건에 맞는 모든 tag를 리스트로 반환합니다.
  2. get_text 함수

    • tag안의 value를 추출
    • 부모tag의 경우, 모든 자식 tag의 value를 추출
  3. attribute 값 추출하기

    • 경우에 따라 추출하고자 하는 값이 attribute에도 존재함
    • 이 경우에는 검색한 tag에 attribute 이름을 [ ]연산을 통해 추출가능
    • 예) div.find(‘h3’)[‘title’]
1
2
tag = soup.find('h3')
tag['title']

CSS의 select_one과 select 함수

  • CSS를 이용하여 tag 찾기
    • select, select_one함수 사용
    • css selector 사용법
      • 태그명 찾기 tag
      • 자손 태그 찾기 - 자손 관계 (tag tag)
      • 자식 태그 찾기 - 다이렉트 자식 관계 (tag > tag)
      • 아이디 찾기 #id
      • 클래스 찾기 .class
      • 속성값 찾기 [name=’test’]
        • 속성값 prefix 찾기 [name ^=’test’]
        • 속성값 suffix 찾기 [name $=’test’]
        • 속성값 substring 찾기 [name *=’test]
      • n번째 자식 tag 찾기 :nth-child(n)

webcrawling

※ 출처 : fast campus 머신러닝 인강(변영효 강사님)

  • 일부 내용만 발췌하였고, 기본적인 개념 및 추가내용을 확인하시려면 인강 수강을 권장드립니다.

  • 내용요약

    1. 웹사이트에서 원하는 정보의 태그를 파악
    1. 모듈을 통해 태그를 찾은 후 원하는 값을 가져옴

2. HTML(Hyper Text Markup Language)

  • 웹 사이트를 생성하기 위한 언어로 문서와 문서가 링크로 연결되어 있고, 태그를 사용하는 언어

  • 태그 : HTML 문서의 기본 블락

  • 브라우저에 어떻게 렌더링(화면에 표시)될지 전달

  • <태그명 속성1=”속성값1” 속성2=”속성값2”>Value</태그명>

  • <태그명 속성1=”속성값1” 속성2=”속성값2”/>

  • p 태그 : paragraph tag

    한 문단으로 표시해주는 태그

  • div 태그

    그룹핑을 하는 태그
  • 대부분의 crawling은 태그 안에 있는 값을 추출하는 작업입니다.

  • html 기본구조

BeautifulSoup test

Contents Title

Test contents

Test Test Test 1

Test Test Test 2

Test Test Test 3

웹 사이트에서 본인에게 필요한 정보를 가져오는 실습을 해보는걸 추천드립니다.

git 기본 개념 및 계정 만들기

git 1번째 내용 : 아래 인프런 무료강의를 시청한 후 작성하였습니다.

인프런 무료강좌 git과 github

  • Git이란?
    형상 관리 시스템(version control system)의 한 종류입니다.
    주로 개발자들이 프로그램과 관련된 파일들을 저장하는데 사용합니다.
    게임의 세이브 포인트와 유사한데, 언제든지 저장 시점으로 되돌아 갈 수 있습니다.

절차

  1. github 가입
  2. github 저장소 생성
  3. github 저장소 클론
  4. 내 컴퓨터의 내 문서 아래에 생성된 프로젝트 디렉토리에서 파일 생성 및 작업 완료
  5. 커밋할 파일들 선택해서 스테이지에 올리기(add)
  6. 커밋하기(commit)
  7. 푸시하기(push)
  8. github.com/내아이디/내프로젝트 링크를 통해서 정상적으로 업로드 되어있는지 확인

3가지 기본 개념

  1. 커밋(commit)
    게임의 세이브에 해당하는 행동입니다.
    언제든지 커밋한 시점으로 돌아갈 수 있습니다.
    저장을 원하는 파일들을 묶어서 커밋 명령을 수행하면 됩니다.
  2. 스테이지에 올린다(add)
    커밋전에 저장을 원하는 파일들을 묶는 일입니다.
    스테이지에 파일을 올리는 작업, 이를 간단히 줄여서 add라고 합니다.
  3. github에 업로드(push)
    커밋을 하면 현재 작업 내용의 세이브 데이터가 내 컴퓨터에 저장됩니다.
    github에 업로드하면 게임의 원격 저장과 비슷한 일을 합니다.
    다른 사람과 공유할 수 있고, 내 컴퓨터의 데이터가 날아가도 안전하게 다시 복구할 수 있습니다.
    github에 업로드 하는걸 git에서는 “push”라고 합니다.

직접 해 보기
github.com 가입
http://github.com/join을 통해 깃헙 가입을 합니다.
주의사항

  1. github은 아이디 노출도가 높습니다.
    개발자스러운 아이디를 신중하게 정합니다.
  2. 가입 후 이메일 인증을 해야 가입이 완료됩니다.

참고사항
github의 캐릭터는 옥토캣입니다.
개발자들 사이에서 인기가 매우 높다고 합니다.
옥토캣 이미지

저장소 생성
가입 후 저장소를 하나 만듭니다.

  1. Repository name 입력합니다.
  2. Description을 적습니다.
  3. Public으로 공개 설정합니다.
  4. initialize this repository with a README 박스를 체크합니다.
  5. Create repository, 초록색 new repository 버튼을 눌러서 저장소를 만듭니다.

terminal_basic_command

Mac Terminal basic command

  • zsh : 2020 mac 기본 터미널
  • iTrem : 현재 사용하고 있는 터미널
  • oh my zsh : 터미널 설정 오픈소스

    출처

1. 현재위치 파악 및 폴더 이동하기

1
2
3
4
5
6
7
8
1. $ pwd          : 현재 위치의 절대 경로
2. $ ll : 폴더 자세히 보기
$ ls -al : 모든 폴더 상세내용 표시
$ ls : 폴더 보기
$ cd Documents : Documents 폴더로 이동
$ cd .. : 상위 폴더로 이동
$ cd Music/Music : Music 폴더안의 Music 폴더로 이동
$ cd ~ : 최상위로 이동

2. 폴더, 파일 생성 및 삭제 명령어

참고사항 : 아래 $ 표시가 없는 esc는 esc를 입력하라는 것이 아닌 esc키를 누르라는 의미입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
1. $ mkdir test  : test 폴더 생성
2. $ cd test : test 폴더로 이동
3. $ touch test : test 파일 생성
$ vi test : test 파일 edit
$ i : 터미널 창 왼쪽 아래 - - INSERT - - 라고 보이며, vi editor 내부에 글자를 적을 수 있다.
$ :set nu : 에디터 라인에 숫자 표시
$ test : 테스트라고 작성해봅시다! 혹은 아무글자나 작성해봅시다.
esc : esc키를 누르면 왼쪽 아래 - - INSERT - - 가 사라집니다.
$ :wq : 작성한 내용을 저장 후 빠져나옵니다.
$ rm test : test 파일 삭제
$ ll
$ cd ..
$ rmdir test : test 폴더 삭제
1
2
$ ls  : 현재 경로에 있는 폴더, 파일을 보여줍니다.
$ ll : 현재 경로에 있는 폴더, 파일을 보여줍니다. 단, ls보다 상세하게 보여줍니다. (권한, 사용자계정 등)

3. 터미널에서 사용한 명령어 기록확인 및 실행

  • 사용한 명령어가 기억나지 않거나, 명령어가 길어서 다시 입력하기 귀찮을 경우 사용
1
2
$ history  : 과거에 입력한 명령어 보기
$ !27 : 27번에 실행했던 명령어 다시 실행하기

4. 설치된 판다스 0.24.2버전으로 변경하기

1
2
$ pip uninstall pandas
$ pip install pandas==0.24.2

5. 절대경로

1
$ pwd
  • /home/ubuntu/python3/database : 현재 위치의 절대 경로
  • / : 루트

6. 절대경로로 이동

1
$ cd /home/ubuntu/python3/daatabase/

7. 최상위 계정 접속

1
$ cd /
  • / : 결과

8. 접속되어있는 계정의 최상위 디렉토리로 이동

1
$ cd ~
  • /home/ubuntu : 결과

9. 상대경로

1
$ cd ./python3/database/
  • ./ : 현재위치, 생략가능
1
$ cd ../notebook/  :위로 한단계 올라간 후 노트북 디렉토리로 이동

새로운 명령어를 알게될 때마다 추가 업데이트할 예정입니다.

오탈자 및 조언 댓글로 남겨주세요.
감사합니다 :)

Agile

Agile(애자일) 방법론 3줄 요약

  • 작업 계획을 짧은 단위로 세우고
  • 시제품을 만들어 나가는 사이클을 반복
  • 고객의 요구 변화에 유연하고도 신속하게 대응하는 개발 방법론

    자료출처

요즘 에자일, 에자일 기법에 대해 2번 이상 듣기도 하고 기업에서도 에자일 기법으로 프로세스를 진행한다고 하니 무엇인지에 대한 개념도 알아보고 내 삶에 적용 시키기도 해볼 겸해서 간단하게 기록하려합니다.

우선, 애자일 방법이란 ?
기업경영 및 소프트웨어 등의 개발을 고객중심으로 진행하는 방법론

애자일방식의 조직운영

  1. 고객중심
  2. 아웃풋 중심
  3. 유연하고도 민첩한 대응력
  4. 자율성과 권한을 가진 조직 운영

에자일 개발 선언문

애자일 방법은 급변화하고 진화하고 있는 환경에 효과적으로 대응할 수 있는 방법이라고 생각됩니다.

피드백 댓글로 남겨주세요~!
감사합니다 :)