Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

비열하지 마십시오:통계적 평균 및 백분위수 101

성능 모니터링은 성공적인 애플리케이션 실행의 중요한 부분입니다. 무언가의 성과를 알려주는 가장 기본적인 방법 중 하나 발생할 때마다 지속 시간을 측정하고 통계를 추출하는 것입니다.

평균

값 모음의 평균 또는 평균은 어떤 것이 얼마나 좋은지 나쁜지 알아보는 좋은 출발점입니다. 고려 중인 모든 값을 합한 다음 발생 횟수로 나누어 계산합니다.

Ruby에서 평균 응답 시간을 계산하면 다음과 같습니다.

def mean(array)
 (array.sum.to_f / array.length).round(2)
end
 
durations = [1,2,3,4,5,6,7,8,9,0]
mean(durations) #=> 4.5

참고 :예제에서 나누었을 때 보다 정확한 결과를 얻기 위해 총 지속 시간 값을 Float로 캐스팅했습니다. 그렇지 않으면 Ruby는 가장 가까운 정수로 내림하여 4를 반환합니다. 대신.

중앙값

또 다른 유용한 통계는 중앙값입니다. 비슷하게 들리지만 값 모음의 평균과 중앙값에는 차이가 있습니다.

중앙값은 집합의 위쪽 절반과 아래쪽 절반을 구분하는 값입니다.

값이 홀수인 데이터세트의 경우 먼저 값을 정렬한 다음 중간 숫자를 선택하여 중앙값을 얻습니다. 값이 짝수인 집합의 경우 정렬 후 중앙값은 두 중간 숫자의 평균이 됩니다.

def median(array)
  sorted_array = array.sort
  length = sorted_array.length
 
  if length.odd? # Middle number for odd arrays
    sorted_array[length / 2]
  else # Mean of two middle numbers
    first_value = sorted_array[length / 2]
    second_value = sorted_array[length / 2 - 1]
    (first_value + second_value) / 2.to_f
  end
end
 
# Even array
durations = [1,2,3,4,5,6,7,8,9,0]
median(durations) #=> 4.5
 
# Odd array
durations = [1,1,2,3,4,5,6,7,8,9,0]
median(durations) #=> 4

이 통계는 데이터에 큰 왜곡이 있는지 또는 긴 꼬리가 있는지 확인하는 좋은 방법입니다.

durations = [1,2,3,4,5,2000]
 
median(durations) #=> 3.5
mean(durations) #=> 335.83

위 기간의 평균은 335.83입니다. 2000ms의 단일 이상값 때문입니다. 3.5에 불과한 중앙값 , 왜곡이 있음을 나타냅니다.

데이터 세트의 평균과 중앙값을 모두 계산하면 큰 이상값이나 긴 꼬리가 있는지 파악할 수 있습니다.

평균의 문제

평균과 중앙값은 성과를 나타내는 좋은 지표이지만 전체 내용을 말해주지는 않습니다. 웹페이지를 열 번 요청하면 평균이 매우 낮을 수 있지만 하나 이상의 요청을 완료하는 데 여전히 매우 오랜 시간이 걸릴 수 있습니다.

아래 이미지는 AppSignal에서 특정 동작에 대한 99번째(파란색) 및 90번째(녹색) 백분위수와 평균(빨간색)을 보여줍니다. 99번째와 90번째는 평균에서 상당히 떨어져 있고 약간의 스파이크가 있음을 알 수 있습니다. 즉, 일반 고객은 좋은 경험을 하고 있지만 때때로 페이지가 렌더링될 때까지 거의 두 배의 시간을 기다려야 하는 사용자가 있습니다. 이상적으로는 이러한 모든 값을 가능한 한 서로 가깝게 가져와 모든 사용자에게 보다 일관된 경험을 제공하는 것이 좋습니다.

예를 들어, 10명의 고객이 100밀리초에서 1초 사이의 지속 시간을 가진 페이지를 요청하는 다음 지속 시간 세트가 있다고 가정해 보겠습니다.

[100,100,100,100,100,100,100,100,100,1_000]

결과적으로 190ms의 평균이 됩니다. 한 사용자는 1초 응답 시간이라는 매우 나쁜 경험을 했습니다. 평균만 추적하면 웹사이트의 성능이 뛰어나다고 생각하기 쉬우나 실제로는 때때로 사용자가 끔찍한 경험을 하는 경우가 있습니다.

위의 예는 10개의 요청에 대한 것입니다. 그러나 하루에 1,000개의 요청이 있었다면 해당 사용자 중 100명이 끔찍한 경험을 했다는 것을 의미한다고 상상해 보십시오.

백분위수

값의 분포를 더 잘 이해하기 위해 백분위수를 사용합니다. 백분위수는 중앙값과 유사합니다. 즉, 집합의 절반이 숫자 아래에 있고 절반이 위에 있는 데이터 세트의 한 지점을 나타내는 숫자입니다. 백분위수는 20번째 백분위수가 데이터 세트에 있는 숫자의 20%가 해당 숫자보다 낮다는 것을 의미한다는 점에서 유사합니다.

다음(정렬된) 집합이 주어지면:

[100,100,200,200,300,300,400,400,500,5_000]

20번째 백분위수를 알고 싶다면 다음과 같이 계산할 수 있습니다. 집합에 10개의 값이 있습니다. 원하는 값은 위치 1에 있습니다(20.0 / 100 * 10 - 1). ) 배열이 0에서 시작하기 때문입니다. 이 배열에는 짝수의 항목이 포함되어 있으므로 인덱스(2 ) 및 색인 + 1(3 ). 그러면 150 값이 됩니다. 20번째 백분위수.

매우 순진한 Ruby 구현은 다음과 같습니다.

def percentile(array, wanted_percentile)
  sorted_array = array.sort
 
  index = (wanted_percentile.to_f / 100) * sorted_array.length - 1
 
  # Check if index is not a round number
  if index != index.to_i
    sorted_array.at(index.ceil)
  elsif sorted_array.length.even?
    first_value = sorted_array.at(index)
    second_value = sorted_array.at(index + 1)
    (first_value + second_value) / 2
  else
    sorted_array.at(index)
  end
end
 
# An array with an odd amount of numbers
durations = [100,200,200,300,300,400,400,500,5_000]
 
percentile(durations, 20) #=> 100
percentile(durations, 90) #=> 500
percentile(durations, 95) #=> 5000, index is a fraction, 9.5 the rounded index is 10
 
# An array with an even amount of numbers
durations = [100,100,200,200,300,300,400,400,500,5_000]
 
percentile(durations, 20) #=> 150, average of index 1 & 2 `(100 + 200) / 2`
percentile(durations, 90) #=> 2750, average of index 8 & 9 `(500 + 5000) / 2
percentile(durations, 95) #=> 500, index is a fraction, 8.55 the index is 9

percentile 함수는 median과 매우 유사해 보입니다. 계산 및 실제로 median 50th와 동일 백분위수.

durations = [1,2,3]
 
percentile(durations, 50) == median(durations) #=> true

AppSignal은 위의 통계를 사용하여 애플리케이션에 대한 성능 메트릭을 생성합니다. 우리는 평균/평균에만 의존하지 않고 90번째 및 95번째 백분위수를 계산하여 요청 분포에 대한 더 나은 아이디어를 제공하는 이상값을 표시합니다. 공연 둘러보기 페이지에서 자세히 알아보세요.

이상함

백분위수와 평균이 계산되는 방식 때문에, 예를 들어 다음 데이터 세트에서 90번째 백분위수가 평균 아래로 떨어지는 경우가 있습니다.

durations = [1,1,1,1,1,1,1,1,1,1,2000]
 
percentile(durations, 90) #=> 1
mean(durations) #=> 182.73

이것은 우리에게 mean을 줄 것입니다. 182.73의 , 그리고 1의 90번째 백분위수 .

측정항목 수집 시스템이 90번째 백분위수와 평균만 표시하는 경우에도 90번째 백분위수가 평균 아래로 떨어지면 데이터 세트 어딘가에 엄청난 이상치가 있다는 것을 추론할 수 있습니다.

<블록 인용>

👋 이 기사가 마음에 드시면 Ruby(on Rails) 성능에 대해 더 많이 썼습니다. Ruby 성능 모니터링 체크리스트를 확인하세요.

이 게시물의 거의 100%에 도달했습니다.

지금은 그게 다야! 다른 게시물에서는 Quantile을 사용하여 모든 고객의 요청에 대한 백분위수를 효율적으로 저장하고 계산하는 방법에 대해 이야기할 것입니다. 통계 및 APM, 오류 추적 또는 성능 모니터링에 대한 질문이나 의견이 있는 경우 Twitter @AppSignal 또는 이메일을 통해 문의하십시오.