[중급] 가볍게 이것저것

산업 현장에서 다루는 데이터 분석하기

PassionPython 2019. 8. 14. 18:39
'''
Acknowledgements

This dataset is publicly available for anyone to use under the following terms.

von Birgelen, Alexander; Buratti, Davide; Mager, Jens; Niggemann, Oliver: Self-Organizing Maps for Anomaly Localization and Predictive Maintenance in Cyber-Physical Production Systems. In: 51st CIRP Conference on Manufacturing Systems (CIRP CMS 2018) CIRP-CMS, May 2018.

Paper available open access: https://authors.elsevier.com/sd/article/S221282711830307X

IMPROVE has received funding from the European Union's Horizon 2020 research and innovation programme under Grant Agreement No. 678867

'''
print('\n')

New vs worn cutting blade data

데이터분석과 시각화

OCME Shrink-wrap packers VEGA

이 데이터 셋은 OCME 사의 VEGA(포장장비) 센서 데이터 입니다.

VEGA 장비는 식품 및 음료 산업의 대규모 생산 라인에 배치됩니다.

이 기계는 병이나 캔을 정해진 크기로 묶어서 필름으로 포장 한 다음, 필름을 열 수축시켜 패키지로 결합합니다.

플라스틱 필름은 대형 스풀로 기계에 공급 된 다음 제품 포장지에 필름을 감쌀 수있는 길이로 자릅니다.

절단 블레이드를 올바르게 설정하고 유지 보수해야합니다.

또한, 블레이드가 금속 하우징 내에 봉입되어 있고 회전 속도가 빠르기 때문에 작동 중에 블레이드를 육안으로 검사 할 수 없습니다.

블레이드 성능 저하를 모니터링하면 기계 신뢰성이 향상되고 절단 실패로 인한 예기치 않은 장비 다운 시간이 줄어 듭니다.

우리는 새 블레이드와 완전히 마모 된 블레이드를 비교하는 두 가지 데이터를 알아보고자 합니다.

컬럼 설명

(데이터는 다음의 링크에서 다운받으실 수 있습니다)
Kaggle 회원가입이 필요합니다(무료)
  • Timestamp

    • 초 단위의 타임스탬프
  • pCut Motor

    • 회전 토크 발생을 위한 모터의 토크(돌림 힘)
  • pCut CTRL Position controller: Lag error

    • 경로 생성기의 설정 점과 블레이드의 실제 현재 엔코더 위치 사이의 순간 위치 오차
  • pCut CTRL Position controller: Actual position

    • 해당 순간 블레이드의 위치
  • pCut CTRL Position controller: Actual speed

    • 해당 순간 블레이드의 속도
  • pSvolFilm CTRL Position controller: Actual position

    • 해당 순간 언와인더(필름을 연속적으로 풀어주며 공급하는 장치) 의 위치
  • pSvolFilm CTRL Position controller: Actual speed

    • 해당 순간 언와인더의 속도
  • pSvolFilm CTRL Position controller: Lag error

    • 경로 생성기의 설정 점과 언와인더의 실제 현재 엔코더 위치 사이의 순간 위치 오차
# 파이썬의 데이터 분석 도구인 Pandas 를 불러옵니다.
# Pandas 를 쉽게 설명드리면, 파이썬으로 엑셀을 다룰 수 있는 도구라고 볼 수 있습니다.
# 이 도구를 앞으로는 pd라는 축약어로 사용하기로 합니다.

import pandas as pd

Load Dataset

모든 데이터 분석의 시작은 데이터를 읽어오는 것입니다.

파일의 경로를 지정하는 방법에 주의하셔야 합니다.

만일 read_csv를 실행할 때 (FileNotFoundError)라는 이름의 에러가 난다면 경로가 제대로 지정이 되지 않은 것입니다.

다음의 링크에서 경로를 지정하는 법을 다루고 있습니다.

탐색기에서 파일이 있는 경로까지 가서 경로주소를 복사한 다음, path 변수에 문자열로 넣어주세요.

원 표시 또는 역슬래시가 있다면 그것을 슬래시 ( / ) 로 바꿔주세요.
맨 마지막에도 슬래시를 붙여주세요.

예)

C:\Users\one\Desktop\Factory\blade\data 이라면,

C:/Users/one/Desktop/Factory/blade/data/ 로 바꿔서

path = 'C:/Users/one/Desktop/Factory/blade/data/' 이렇게 path 변수에 문자열로 넣어주세요


# 판다스의 read_csv로 NewBlade001.csv, WornBlade001.csv 파일을 읽어옵니다.
# 읽어온 데이터를 각각 new, old 이라는 이름의 변수에 할당합니다.

path = 'C:/Users/one/Desktop/Factory/Factory/Factory_all/blade/data/'

new = pd.read_csv(path+"NewBlade001.csv")
old = pd.read_csv(path+"WornBlade001.csv")
# 새로 불러온 변수 new의 첫 5 개 행을 확인합니다.

new.head()
  Timestamp pCut Motor: Torque pCut CTRL Position controller: Lag error pCut CTRL Position controller: Actual position pCut CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Actual position pSvolFilm CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Lag error
0 -0.188 -0.112131 -0.002490 -884606 0.000000 11128 2.504289 0.261085
1 -0.184 -0.088931 -0.003863 -884606 17.166138 11128 -2.504289 0.260083
2 -0.180 -0.115141 0.001630 -884606 -6.866455 11128 7.513016 0.259081
3 -0.176 -0.111815 0.003003 -884606 -13.732910 11128 -2.504289 0.260083
4 -0.172 -0.130970 0.004376 -884606 -6.866455 11128 0.000000 0.261085
# 새로 불러온 변수 old의 첫 5 개 행을 확인합니다.

old.head()
  Timestamp pCut Motor: Torque pCut CTRL Position controller: Lag error pCut CTRL Position controller: Actual position pCut CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Actual position pSvolFilm CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Lag error
0 -0.188 -0.001146 0.004242 -838822 0.000000 35096 5.008578 0.008014
1 -0.184 0.014685 0.001495 -838822 6.866455 35096 0.000000 0.007012
2 -0.180 0.023437 -0.001251 -838821 13.732910 35096 2.504289 0.008014
3 -0.176 0.016672 -0.001251 -838821 -6.866455 35096 -2.504289 0.007012
4 -0.172 0.002712 0.001495 -838822 -6.866455 35096 -5.008727 0.008014
# .describe() 를 통해 우리는, 기초 통계량을 알 수 있습니다.

new.describe()
  Timestamp pCut Motor: Torque pCut CTRL Position controller: Lag error pCut CTRL Position controller: Actual position pCut CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Actual position pSvolFilm CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Lag error
count 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000
mean 3.906000 -0.077109 -0.000306 -881592.086426 878.606161 16150.827148 1371.617691 0.646027
std 2.365404 0.372693 0.038205 2661.658773 1317.007966 3597.327988 552.624403 0.085916
min -0.188000 -2.292097 -0.513631 -884747.000000 -954.437256 11128.000000 -7.513016 0.259081
25% 1.859000 -0.304018 -0.009814 -884346.250000 -172.519684 12420.500000 743.789139 0.612427
50% 3.906000 -0.165394 -0.000182 -881146.000000 449.752808 16047.000000 1389.908569 0.652235
75% 5.953000 0.319277 0.009876 -879821.500000 2059.078125 19591.000000 1968.411041 0.693293
max 7.999999 0.606260 0.346109 -877098.000000 3556.823730 22360.000000 2068.584717 0.869679
# 새 것과 헌 것의 기초 통계량만 잘 비교해도 수치상 다른 것들이 있죠?

old.describe()
  Timestamp pCut Motor: Torque pCut CTRL Position controller: Lag error pCut CTRL Position controller: Actual position pCut CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Actual position pSvolFilm CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Lag error
count 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000 2048.000000
mean 3.906000 -0.128408 -0.000151 -834777.470703 990.159236 41277.008301 1605.196273 0.649488
std 2.365404 0.434817 0.041539 2815.127626 1318.092062 4097.408290 554.174297 0.116653
min -0.188000 -2.843960 -0.452279 -838822.000000 -954.437256 35096.000000 -7.513016 0.005009
25% 1.859000 -0.366514 -0.012547 -837605.250000 -164.794922 37357.250000 1379.891235 0.628955
50% 3.906000 -0.191581 -0.000255 -834328.500000 911.521912 41240.000000 1402.430298 0.663509
75% 5.953000 0.288852 0.012403 -832540.750000 2059.936523 45164.750000 2005.976196 0.701098
max 7.999999 0.611113 0.300557 -830298.000000 3570.556641 48242.000000 2742.251953 0.935391

Combine Dataset

new 와 old 를 하나의 데이터프레임으로 묶기

# label 컬럼을 새로 만들어, new 는 0을, old 는 1 을 할당합니다.
# 섞였을 때 서로 구분하기 위함입니다.

new['label'] = 0
old['label'] = 1
# pd.concat() 안에 리스트 형태로, 묶고자 하는 데이터프레임들을 넣어주면 됩니다.
# 왼쪽(new) 의 아래에 오른쪽(old) 이 붙는다고 생각하시면 됩니다.
# 이어붙인 값을 = 을 통해 combined 라는 변수에 할당합니다.
combined = pd.concat([new, old])

# 잘 실행되었는지 확인하기 위해 describe() 를 사용합니다.
# 아래에 row 가 추가된 것이므로, count 행이 2048 에서 4096 으로 바뀐 것을 확인하면 됩니다.
combined.describe()
  Timestamp pCut Motor: Torque pCut CTRL Position controller: Lag error pCut CTRL Position controller: Actual position pCut CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Actual position pSvolFilm CTRL Position controller: Actual speed pSvolFilm CTRL Position controller: Lag error label
count 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000
mean 3.906000 -0.102758 -0.000229 -858184.778564 934.382699 28713.917725 1488.406982 0.647758 0.500000
std 2.365115 0.405711 0.039902 23569.868730 1318.569755 13142.713531 565.526059 0.102446 0.500061
min -0.188000 -2.843960 -0.513631 -884747.000000 -954.437256 11128.000000 -7.513016 0.005009 0.000000
25% 1.859000 -0.332745 -0.011033 -881146.000000 -168.228149 16048.500000 1372.378052 0.620462 0.000000
50% 3.906000 -0.173652 -0.000189 -857960.000000 638.580322 28728.000000 1397.421509 0.657892 0.500000
75% 5.953000 0.306975 0.010926 -834328.750000 2059.936523 41238.500000 1993.454224 0.697447 1.000000
max 7.999999 0.611113 0.346109 -830298.000000 3570.556641 48242.000000 2742.251953 0.935391 1.000000
# combined.columns 는 combined 이라는 변수에 담긴 데이터프레임 값의 컬럼들 입니다.
# 이 위치에 = 을 통해 리스트로 내가 새롭게 정하고자 하는 컬럼들을 집어넣을 수 있습니다.
# 기존 컬럼들의 이름이 너무 길어서... 줄여서 넣습니다.

combined.columns = ['Time', 'Torq', "Cut_Err", "Cut_Pos", "Cut_Spd", 
                    "Film_Pos", "Film_Spd", "Film_Err", "label"]
# 기존에 있던 컬럼들의 상호 비교를 위해 모든 변수들과의 관계에 대한 산점도 행렬을 구합니다.
# from pandas.plotting import * 는 판다스 안의 산점도 행렬 함수를 불러오기 위함입니다.
# 산점도 행렬 함수 안에 들어가는 내용은 다음과 같습니다.

# combined -> 보려고 하는 모든 데이터가 있는 데이터프레임
# c -> color 의 약자로, 산점도 안에서 구분하고자 하는 정보, 지금은 combined['label'] 이고, 
# 이는 새 것과 헌 것을 구분합니다.
# figsize -> 보려고 하는 이미지의 크기 정보

from pandas.plotting import *
result = scatter_matrix(combined, 
                        c = combined['label'], 
                        figsize=(25, 25))

파생변수 생성

# 데이터 분석자의 상식과 배경지식을 동원하여, 기존 특징들로부터 도출할 수 있는 새로운 변수입니다.
# 속도라는 물리량이 있고, 초당 250회 획득하는 변수이므로 
# 해당 순간의 힘을 구할 수 있습니다.

# 미분의 개념을 적용합니다.
# 속도의 변화량을 시간변화량으로 나누어 힘을 구합니다.
# 엄밀하게는 비례상수인 m 이 들어가지만, 상수이기 때문에 무시합니다.

# 새로운 파생변수의 이름을 Force 가 아닌 Acc(가속도) 로 해도 무관합니다.
# .diff(1) 을 하여, 이전 row 와의 차이를 구하고
# 데이터 획득 주기인 0.004 초로 나눕니다.

# .loc[combined['label'] == 0] 은 new blade 안에서만 연산하기 위함입니다.
# .loc[combined['label'] == 1] 은 old blade 안에서만 연산하기 위함입니다.

# Blade 기준으로는 Cut_ 수식어를, 언와인더 기준으로는 Film_ 을 붙여줍니다.

combined['Cut_Force'] = combined["Cut_Spd"].loc[combined["label"] == 0].diff(1) / 0.004
combined['Cut_Force'] = combined["Cut_Spd"].loc[combined["label"] == 1].diff(1) / 0.004

combined['Film_Force'] = combined["Film_Spd"].loc[combined["label"] == 0].diff(1) / 0.004
combined['Film_Force'] = combined["Film_Spd"].loc[combined["label"] == 1].diff(1) / 0.004

# .fillna(0) 을 하는 이유는, 첫 번째 행(row) 의 경우 이전 값이 존재하지 않기 때문에 
# NaN 이 있으므로, 이를 채워주기 위함입니다.

combined.fillna(0, inplace=True)
# 잘 연산되었는지 확인하기 위해 .head() 로 첫 5 번째 행들을 알아봅니다.

combined[["Cut_Force", "Film_Force", 'label']].head()
  Cut_Force Film_Force label
0 0.000000 0.000000 0
1 1716.613769 -1252.144575 0
2 1716.613770 626.072288 0
3 -5149.841309 -1252.144575 0
4 0.000000 -626.109481 0

정규화(Normalization)

# for 문으로 데이터프레임을 뽑아오면 컬럼명이 나오게 됩니다.
# 컬럼명을 키워드로 하여 해당 컬럼의 모든 열을 가져올 수 있습니다.
# 이 부분은 combined[i] 이고, 여기서 i 는 컬럼의 이름 입니다.


# 아래 작업을 모든 컬럼에 대해 반복합니다.
for i in combined:
    # 한 컬럼에서 최소값과 최대값을 구하고
    # 해당 컬럼의 모든 열 값에서 최소값을 뺍니다.
    # 그리고 이를 최대값으로 나누게 되면, 모든 값들이 0 에서 1 사이의 값으로 정규화 됩니다.
    minimum = combined[i].min()
    maximum = combined[i].max()
    # 최대값이 아닌, minimum 을 뺀 값으로 나누는 이유는 
    # 모든 열에서 minimum 을 뺀 시점에서 이미 최대값이 minimum 만큼 감소하기 때문입니다.
    combined[i] = (combined[i] - minimum) / (maximum - minimum)

combined.describe()
  Time Torq Cut_Err Cut_Pos Cut_Spd Film_Pos Film_Spd Film_Err label Cut_Force Film_Force
count 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000 4096.000000
mean 0.500000 0.793385 0.597160 0.487837 0.417419 0.473835 0.544017 0.690843 0.500000 0.544433 0.465547
std 0.288851 0.117425 0.046412 0.432880 0.291397 0.354117 0.205663 0.110112 0.500061 0.090624 0.134542
min 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 0.250000 0.726820 0.584593 0.066135 0.173748 0.132578 0.501821 0.661506 0.000000 0.493506 0.377048
50% 0.500000 0.772866 0.597206 0.491965 0.352049 0.474215 0.510929 0.701737 0.500000 0.532467 0.475409
75% 0.750000 0.911973 0.610135 0.925972 0.666161 0.811298 0.727687 0.744251 1.000000 0.597402 0.557376
max 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000
# 블레이드와 언와인더에 작용하는 해당 순간의 Force 정보를 만들었으니
# 이를 모든 변수들과 산점도 행렬을 통해 비교합니다.

# 여기에서 볼 사항은 

# Time 과 다른 변수들간의 그래프
# 새롭게 만든 Force 와 다른 변수들간의 그래프 입니다.

from pandas.plotting import *
result = scatter_matrix(combined, 
                        c = combined['label'], 
                        figsize=(25, 25))

# 데이터 시각화 도구인 matplotlib를 불러와 이를 줄여 plt라고 사용합니다.
import matplotlib.pyplot as plt

# Time 과 다른 변수들간의 산점도를 lineplot 으로 확인하고자 합니다.
# 그래프가 y 축만 다르고 나머지가 동일하기 때문에
# 반복을 줄이기 위해 그래프를 그려주는 함수를 만들어줍니다.

def line_plot(y, log=False):
    # y 값만 입력받는 함수입니다.
    # .loc[combined['label'] == 0] 
    x = "Time"
    x1 = combined[x].loc[combined['label'] == 0]
    y1 = combined[y].loc[combined['label'] == 0]

    x2 = combined[x].loc[combined['label'] == 1]
    y2 = combined[y].loc[combined['label'] == 1]

    plt.plot(x1, y1, label= y + "_New") 
    plt.plot(x2, y2, label= y + "_Old")

    if log:
        plt.yscale('log')
    plt.legend(loc='lower left')
    plt.show()
line_plot("Torq")

target_cols = ['Torq', "Cut_Err", "Cut_Pos", "Cut_Spd", "Cut_Force", 
               "Film_Pos", "Film_Spd", "Film_Err", "Film_Force"]

for target in target_cols:
    line_plot(target)



























# 함수의 입력값으로 log=True 를 넣어서 로그스케일로 확인합니다.
line_plot("Cut_Force", log=True)

# matplotlib의 subplots를 사용합니다. 이 함수는 여러 개의 시각화를 한 화면에 띄울 수 있도록 합니다.
# 이번에는 2x2으로 총 4개의 시각화를 한 화면에 띄웁니다.

figure, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)

# 시각화의 전체 사이즈는 18x8로 설정합니다.
figure.set_size_inches(18, 15)

combined.plot.scatter(x = 'Film_Force', y = 'Film_Spd', c = 'label', colormap = 'viridis', ax=ax1)
combined.plot.scatter(x = 'Film_Force', y = 'Cut_Spd', c = 'label', colormap = 'viridis', ax=ax2)
combined.plot.scatter(x = 'Cut_Force', y = 'Film_Spd', c = 'label', colormap = 'viridis', ax=ax3)
combined.plot.scatter(x = 'Cut_Force', y = 'Cut_Spd', c = 'label', colormap = 'viridis', ax=ax4)
<matplotlib.axes._subplots.AxesSubplot at 0x2d8033110f0>