정보

    • 업무명     : 직달 일사량 자료를 이용한 일조량 산출 및 비교 분석

    • 작성자     : 이상호

    • 작성일     : 2020-01-06

    • 설   명      :

    • 수정이력 :

     

     내용

    [특징]

    • 직달 일사량 및 일조량을 이해하기 위해서 가시화 및 비교 분석이 요구되며 이 프로그램은 이러한 목적을 달성하기 위한 소프트웨어

     

    [기능]

    • 직달 일사량을 이용하여 일조량 산출 및 비교 분석

     

    [활용 자료]

    • 자료명 : 단파 복사 및 장파 복사 자료

    • 자료 종류 : 직달 일사량, 일조량

    • 확장자 : dat

    • 기간 : 2012년 10월 11일 - 2017년 07월 05일

    • 자료 해상도 : 1초 샘플링하여 1분 평균

    • 제공처 : 강릉원주대 생명대학 2호관 옥상 (복사 관측소)

    • 자세한 정보는 아래 링크 참조

     

    [Fortran, Gnuplot, ShellScript] 기상 자료를 이용한 통계 분석 및 가시화

    정보 업무명 : 기상 자료를 이용한 통계 분석 및 가시화 작성자 : 이상호 작성일 : 2019-08-31 설 명 : 수정이력 : 내용 [특징] 기상 자료의 특성을 파악하기 위해 통계 분석 및 가시화 도구가 필요하며 이 프로그..

    shlee1990.tistory.com

     

    [자료 처리 방안 및 활용 분석 기법]

    • 없음

     

    [사용법]

    • 입력 자료를 동일 디렉터리 위치

    • 소스 코드를 실행 (Rscript
      Retrieval_and_Comparison_Analysis_of_Sunshine_Duration_Using_Pyrheliometer_Data.R)

    • 가시화 결과를 확인

     

    [사용 OS]

    • Windows 10

     

    [사용 언어]

    • R v3.6.2

    • R Studio v1.2.5033

     

     소스 코드

    [명세]

    • 전역 설정

      • 최대 10 자리 설정

      • 메모리 해제

    # Set Option
    options(digits = 10)
    memory.limit(size = 9999999999999)

     

    • 라이브러리 읽기

    # Library Load
    library(tidyverse)
    library(data.table)
    library(lubridate)
    library(timeDate)
    library(extrafont)
    library(colorRamps)
    library(RColorBrewer)

     

    • 파일 읽기

    # File Read
    dfData = fread("total.dat")
    colnames(dfData) = c("st", "year", "month", "day", "hour", "minute", "press", "geo", "temp", "dewpt", "dir", "wspd", NA, NA, NA)
    
    dplyr::glimpse(dfData)
    

     

    • 함수 선언

    # Function Load
    fnStatResult = function(xAxis, yAxis) {
       
       if (length(yAxis) > 0) {
          nSlope = coef(lm(yAxis ~ xAxis))[2]
          nInterp = coef(lm(yAxis ~ xAxis))[1]
          nMeanX = mean(xAxis, na.rm = TRUE)
          nMeanY = mean(yAxis, na.rm = TRUE)
          nSdX = sd(xAxis, na.rm=  TRUE)
          nSdY = sd(yAxis, na.rm = TRUE)
          nNumber = length(yAxis)
          nBias = mean(xAxis - yAxis, na.rm = TRUE)
          nRelBias = (nBias / mean(yAxis, na.rm = TRUE))*100.0
          # nRelBias = (nBias / mean(xAxis, na.rm = TRUE)) * 100.0
          nRmse = sqrt(mean((xAxis - yAxis)^2, na.rm = TRUE))
          nRelRmse = (nRmse / mean(yAxis, na.rm = TRUE)) * 100.0
          # nRelRmse = (nRmse / mean(xAxis, na.rm = TRUE)) * 100.0
          nR = cor(xAxis, yAxis)
          nMeanDiff = mean(xAxis - yAxis, na.rm = TRUE)
          nSdDiff = sd(xAxis - yAxis, na.rm = TRUE)
          nPerMeanDiff = mean((xAxis - yAxis) / yAxis, na.rm = TRUE) * 100.0
          nPvalue = cor.test(xAxis, yAxis)$p.value
          
          return( c(nSlope, nInterp, nMeanX, nMeanY, nSdX, nSdY, nNumber, nBias, nRelBias, nRmse, nRelRmse, nR, nMeanDiff, nPerMeanDiff, nPerMeanDiff, nPvalue) )
       }
    }

     

    • 단파복사 자료 읽기

    # Shortwave Radiation File Read
    dfCr1000Ver1 = data.table::fread("20121011-20140623.dat", header = FALSE, sep = ",", skip = 4)
    dfCr1000Ver2 = data.table::fread("20140624-20151217.dat", header = FALSE, sep = ",", skip = 4)
    dfCr1000Ver3 = data.table::fread("CR1000_Table1.dat", header = FALSE, sep = ",", skip = 4)
    
    dfCr1000Sw = dplyr::bind_rows(dfCr1000Ver1, dfCr1000Ver2, dfCr1000Ver3)    
    
    dplyr::tbl_df(dfCr1000Sw)

     

     

    • 장파복사 자료 읽기

    # Longwave Radiation File Read
    dfCr1000Ir = fread("CR1000_2_Table1.dat", header = FALSE, sep = ",", skip = 4)
    
    dplyr::tbl_df(dfCr1000Ir)

     

     

    • Data Frame 설정

      • 장파복사 (dfCr1000Ir) 좌측 조인

      • 연-월-일 시:분:초를 날짜 형태로 변환

    # Set Data Frame
    dfData = dfCr1000Sw %>%
       dplyr::left_join(dfCr1000Ir, by = "V1") %>%
       dplyr::mutate(
          dtDateTime = readr::parse_datetime(V1, format = "%Y-%m-%d %H:%M:%S")
          , nYear = lubridate::year(dtDate)
          , nMonth = lubridate::month(dtDate)
          , nDay = lubridate::day(dtDate)
          , nHour = lubridate::hour(dtDate)
          , nMinute = lubridate::minute(dtDate)
          , nSec = lubridate::second(dtDate)
          , nLastDayInMonth = lubridate::day(timeDate::timeLastDayInMonth(format(dtDate, "%Y%m%d"), format = "%Y%m%d", zone = "Asia/Seoul"))
          , xranYmdHms = 
             nYear + ((nMonth - 1) / 12.0) + ((nDay - 1) / (12.0 * nLastDayInMonth))  + (nHour / (12.0 * nLastDayInMonth * 24.0))
             + (nMinute / (12.0 * nLastDayInMonth * 24.0 * 60.0))
             + (nSec / (12.0 * nLastDayInMonth * 24.0 * 60.0 * 60.0))
          , xranYmd = nYear + ((nMonth - 1) / 12.0) + ((nDay - 1) / (12.0 * nLastDayInMonth))
          )
    
    # Set Coloum Name
    colnames(dfData) = c("datetime", "count_sw", "temp", "press", "A", "B", "C", "D", "E", "global", "F", "diffuse", "direct", "G", "hum", "ws", "wd", "H", "ssmc", "ssdc", "count_ir", "CGR3NetRadiation", "dlr", "CGR3Temp_AVG", "CGR3Kel_AVG", "BattV_Min", "dtDateTime", "nYear", "nMonth", "nDay", "nHour", "nMinute", "nSec", "nLastDayInMonth", "xranYmdHms", "xranYmd")
    # count(관측 기록 횟수), temp(외부 온도), press(관측소 기압), Awm(전천일사), Cwm(산란일사),
    # Dwm(직달일사), Hum(상대 습도), ws(풍속), wd(풍향), ssmc(분누적 일조 : 횟수), ssdc(일누적 일조 : 시간)
    # CGR3NetRadiation (순복사), dlr(하향장파복사), CGR3Temp_AVG(섭씨 온도), CGR3Kel_AVG(켈빈 온도)
    
    dplyr::glimpse(dfData)

     

     

    • Filter Data Frame 설정

      • 제외할 사례 선정

    # Set Filter Data Frame
    dfFilterDate = data.frame(
       sDateYm = c(
          "2016-03"
          , "2016-06"
          , "2016-09"
          , "2016-12"
          )
    )

     

    • Data Frame를 통해 L1 전처리

      • 열 선택 (dtDateTime, nYear, nMonth, nDay, nHour, nMinute, nSec, nLastDayInMonth, xranYmdHms, temp, press, hum, ws, wd, ssmc, ssdc, global, diffuse, direct, dlr)

      • 사례 제외 그리고 전천/산란/직달 일사량 최대값 및 최소값 설정

    # L1 Processing Using Data Frame
    dfDataL1 = dfData %>%
       dplyr::select(dtDateTime, nYear, nMonth, nDay, nHour, nMinute, nSec, nLastDayInMonth, xranYmdHms, temp, press, hum, ws, wd, ssmc, ssdc, global, diffuse, direct, dlr) %>%
       dplyr::mutate(sDateYm = format(dtDateTime, "%Y-%m")) %>%
       dplyr::filter(
          ! sDateYm %in% dfFilterDate$sDateYm
          , dplyr::between(global, 0, 4000)
          , dplyr::between(diffuse, 0, 4000)
          , dplyr::between(direct, 0, 4000)
          )
          
    dplyr::tbl_df(dfDataL1)

     

     

    • L1 자료를 통해 출력

    # Write Using L1 Data Frame
    data.table::fwrite(
       dfDataL1
       , sep = ","
       , file = "OUTPUT/dfDataL1.csv"
       , append = FALSE
       , row.names = FALSE
       , col.names = TRUE
       , dateTimeAs = "write.csv"
       , na = NA
    )

     

     

    • Filter Data Frame 설정

      • 제외할 사례 선정

    # Set Data Frame
    dfFilterDate = data.frame(
       sDateYmd = c(
          "2013-05-16"
          , "2013-07-07"
          , "2013-09-23"
          , "2014-03-04"
          , "2014-07-08"
          , "2014-09-15"
          , "2015-03-01"
          , "2015-03-02"
          , "2015-04-22"
          , "2015-08-19"
          , "2015-08-31"
          , "2017-02-14"
          , "2017-02-15"
          , "2017-02-16"
          , "2017-02-17"
          , "2017-02-18"
          , "2017-02-19"
          , "2017-02-20"
          , "2017-02-21"
          , "2017-02-22"
          , "2017-02-23"
          , "2017-02-24"
          , "2017-02-25"
          , "2017-02-26"
          , "2017-02-27"
          , "2017-02-28"
          , "2017-02-29"
          , "2017-03-01"
          , "2017-03-02"
          , "2017-03-03"
          , "2017-03-04"
          , "2017-03-05"
          , "2017-03-06"
          , "2017-03-07"
          , "2017-06-02"
          )
    )

     

     

    • Data Frame 자료를 통해 L2 전처리

      • 매 분마다 직달 일사량 (nDirect)이 120 Wm-2 이하일 경우에 대해 0-60으로 선형 내삽

      • 관측소 번호/년/월/기압에 따라 평균, NA 개수, Not NA 개수, 정상화율 계산

    # L2 Processing Using Data Frame
    dfDataL2 = dfData %>%
       dplyr::mutate(
          nSsm = ssmc / 3600.0
          , nDiff = ssdc - lag(ssdc)
          , dtDateYmd = as.Date(dtDateTime)
          , nDirect = dplyr::case_when(
             direct > 120 ~ 120
             , TRUE ~ direct
             )
          ) %>%
       dplyr::filter(
          dplyr::between(nHour, 09, 15)
          , ssmc > 0
          , nDiff > 0
          , nSsm > 0
          , nDirect > 0
          , ! dtDateYmd %in% as.Date(dfFilterDate$sDateYmd)
          ) %>%
       dplyr::mutate(
          nDirectSsmc = approx(c(0, 120), c(0, 60), xout = nDirect)$y
          )
    
    dplyr::glimpse(dfDataL2)

     

     

    • L2 자료를 통해 L3 전처리

      • 연/월/일에 따라 직달 일사량으로부터 일조량, 일조계로부터 일조량, 두 자료의 차이, 최대 직달 일사량, 최대 전천 일사량, 자료 개수를 계산

      • 자료 개수 및 시간에 대해 각각 0-420개 및 2013-2016년으로 설정

    # L3 Processing Using Data Frame L2
    dfDataL3 = dfDataL2 %>%
       dplyr::group_by(nYear, nMonth, nDay, xranYmd) %>%
       dplyr::summarise(
          nSumSsm = sum(ssmc, na.rm = TRUE) / 3600.0
          , nSumDirectSsm = sum(nDirectSsmc, na.rm = TRUE) / 3600.0
          , nSumDiff = sum(nDiff, na.rm = TRUE)
          , nMaxDirect = max(direct, na.rm = TRUE)
          , nMaxGlobal = max(global, na.rm = TRUE)
          , iNumber = n()
          ) %>%
       dplyr::filter(
          dplyr::between(iNumber, 0, 420)
          , dplyr::between(xranYmd, 2013, 2016)
          )
    
    dplyr::glimpse(dfDataL3)

     

     

    • 산점도를 위한 초기 설정

    # Set Value for Visualization  
    xAxis = dfDataL3$nSumDirectSsm
    yAxis = dfDataL3$nSumSsm
    
    xcord = 0.25
    ycord = seq(7.75, 0, -0.5)
    cbSpectral = rev(RColorBrewer::brewer.pal(11, "Spectral"))
    font = "Palatino Linotype"
    nVal = fnStatResult(xAxis, yAxis)  ;  sprintf("%.3f", nVal)

     

    • ggplot2를 이용한 산점도

    # Scatterplot Using ggplot2
    ggplot() +
       coord_fixed(ratio = 1) +
       theme_bw() +
       geom_point(data = dfDataL3, aes(xAxis, yAxis), shape = 21, colour = "black", fill = "white", size = 2.5) +
       annotate("text", x = xcord, y = ycord[1], label = paste0("(Sun.) = ", sprintf("%.2f", nVal[1])," x (Pyr.) + ", sprintf("%.2f", nVal[2])), size = 5, hjust = 0, color = "red", fontface = "bold", family = font) +
       annotate("text", x = xcord, y = ycord[2], label = paste0("R = ", sprintf("%.2f", nVal[12]), " (p-value < ", sprintf("%.3f", nVal[16]), ")"), size = 5, hjust = 0, color = "red", family = font, fontface = "bold") +
       annotate("text", x = xcord, y = ycord[3], label = paste0("Bias = ", sprintf("%.2f", nVal[8]), " (", sprintf("%.2f", nVal[9])," %)"), parse = FALSE, size = 5, hjust = 0, family = font, fontface = "bold") +
       annotate("text", x = xcord, y = ycord[4], label = paste0("RMSE = ", sprintf("%.2f",nVal[10]), " (", sprintf("%.2f", nVal[11])," %)"), parse = FALSE, size = 5, hjust = 0, family = font, fontface = "bold") +
       annotate("text", x = xcord, y = ycord[5], label = paste0("N = ", format(nVal[7], big.mark = ",", scientific = FALSE)), size = 5, hjust = 0, color = "black", family = font, fontface = "bold") +
       geom_abline(intercept = 0, slope = 1, linetype = 1, color = "black", size = 1.0) +
       stat_smooth(data = dfDataL3, aes(xAxis, yAxis), method = "lm", color = "red", se = FALSE) +
       scale_x_continuous(minor_breaks = seq(0, 8, 2), breaks=seq(0, 8, 2),  expand = c(0, 0), limits = c(0, 8)) +
       scale_y_continuous(minor_breaks = seq(0, 8, 2), breaks=seq(0, 8, 2),  expand = c(0, 0), limits = c(0, 8)) +
       labs(
          title = "Relationship  between  Sunshine Record  and\n  Pyrheliometer  of  2013/01/01-2016/12/31"
          , y = expression(paste(bold("Sunshine Duration  by  Sunshine Recorder  [Hr]")))
          , x = expression(paste(bold("Sunshine Duration  by  Pyrheliometer  [Hr]")))
          , fill = "Count"
          ) +
       theme(
          plot.title=element_text(face = "bold", size = 16, color="black", hjust = 0.5)
          , axis.title.x = element_text(face = "bold", size = 16, colour = "black")
          , axis.title.y = element_text(face = "bold", size = 16, colour = "black", angle = 90)
          , axis.text.x = element_text(face = "bold", size = 16, colour = "black")
          , axis.text.y = element_text(face = "bold", size = 16, colour = "black")
          , legend.title=element_text(face = "bold", size = 14, colour = "black")
          , legend.position = c(0, 1), legend.justification = c(0, 0.96)
          , legend.key=element_blank()
          , legend.text=element_text(size = 14, face = "bold")
          , legend.background=element_blank()
          , text=element_text(family = font)
          , plot.margin=unit(c(0, 8, 0, 0), "mm")
          ) +
       ggsave(filename = paste0("FIG/Sunshine Duration.png"), width = 6, height = 6, dpi = 600)

     

    그림. 직달 일사량 및 일조계를 이용하여 일조량 산점도

     

    • 시계열을 위한 초기 설정

    # Set Value for Visualization   
    xAxis = dfDataL3$xranYmd
    
    yAxis = dfDataL3$nSumSsm
    yAxis = dfDataL3$nSumDirectSsm
    
    nVal = fnStatResult(xAxis, yAxis)  ;  sprintf("%.3f", nVal)
    xcord = 2013.05
    ycord = seq(9.5, 0, -1.0)

     

    • ggplot2를 이용한 시계열

    # Time series Using ggplot2
    ggplot() +
       # coord_fixed(ratio = 1) +
       theme_bw() +
       geom_point(data = dfDataL3, aes(xAxis, yAxis), shape = 21, colour = "black", fill = "white", size = 3.5) +
       annotate("text", x = xcord, y = ycord[1], label = paste0("(Sun.) = ", sprintf("%.2f", nVal[1])," x (Year) + ", sprintf("%.2f", nVal[2])), size = 6, hjust = 0, color = "red", fontface = "bold", family = font) +
       annotate("text", x = xcord, y = ycord[2], label = paste0("R = ", sprintf("%.2f", nVal[12]), " (p-value < ", sprintf("%.3f", nVal[16]), ")"), size = 6, hjust = 0, color = "red", family = font, fontface = "bold") +
       annotate("text", x = xcord, y = ycord[3], label = paste0("N = ", format(nVal[7], big.mark = ",", scientific = FALSE)), size = 6, hjust = 0, color = "black", family = font, fontface = "bold") +
       geom_abline(intercept = 0, slope = 1, linetype = 1, color = "black", size = 1.0) +
       stat_smooth(data = dfDataL3, aes(xAxis, yAxis), method = "lm", color = "red", se = FALSE) +
       scale_x_continuous(minor_breaks = seq(2013, 2016, 1), breaks = seq(2013, 2016, 1),  expand=c(0, 0), limits=c(2013,  2016)) +
       scale_y_continuous(minor_breaks = seq(0, 10, 2), breaks = seq(0, 10, 2),  expand = c(0, 0), limits=c(0, 10)) +
       labs(
          # title = "Sunshine Duration  by  Sunshine Recorder  of  2013/01/01-2016/12/31"
          title = "Sunshine Duration  by  Pyrheliometer  of  2013/01/01-2016/12/31"
          , x = expression(paste(bold("Time [Year]")))
          , y = expression(paste(bold("Sunshine Duration  [Hr]")))
          , fill = NULL
          ) +
       theme(
          plot.title = element_text(face = "bold", size = 20, color = "black", hjust = 0.5)
          , axis.title.x = element_text(face = "bold", size = 20, colour = "black")
          , axis.title.y = element_text(face = "bold", size = 20, colour = "black", angle = 90)
          , axis.text.x = element_text(face = "bold", size = 20, colour = "black")
          , axis.text.y = element_text(face = "bold", size = 20, colour = "black")
          , legend.title = element_text(face = "bold", size = 12, colour = "black")
          , legend.position = c(0, 1), legend.justification = c(0, 0.9)
          , legend.key = element_blank()
          , legend.text = element_text(size=12, face="bold")
          , legend.background = element_blank()
          , text=element_text(family = font)
          , plot.margin = unit(c(5, 10, 5, 5), "mm")
          ) +
       # ggsave(filename = paste0("FIG/Sunshine Duration_Time_sun.png"), width = 12, height = 6, dpi = 600)
       ggsave(filename = paste0("FIG/Sunshine Duration_Time_pyr.png"), width = 12, height = 6, dpi = 600)

     

    그림. 직달 일사량을 이용한 일조량 시계열.

     

    그림. 일조계를 이용한 일조량 시계열.

     

    [전체]

     

     참고 문헌

    [논문]

    • 없음

    [보고서]

    • 없음

    [URL]

    • 없음

     

     문의사항

    [기상학/프로그래밍 언어]

    • sangho.lee.1990@gmail.com

    [해양학/천문학/빅데이터]

    • saimang0804@gmail.com
    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기