날짜 요청에서 누락된 값 입력

Nick Mihailovski, Google 애널리틱스 API팀 – 2009년 10월

이 문서에서는 Google 애널리틱스 Data Export API에서 반환된 데이터에서 누락된 시계열 값을 감지하고 백필하는 방법을 설명합니다.


시작하기 전에

이 도움말에서는 사용자가 Google 애널리틱스 데이터 내보내기 API의 작동 방식을 알고 있다고 가정합니다. 샘플 코드는 자바로 되어 있지만 원하는 언어로 개념을 사용할 수 있습니다. 이 문서의 코드는 오픈소스로 제공되며 프로젝트 호스팅에서 다운로드할 수 있습니다.

이 도움말을 읽고 나면 다음 사항을 알 수 있습니다.

  • Google 애널리틱스 데이터 내보내기 API에서 날짜 측정기준을 처리하는 방법입니다.
  • 결과를 그룹화하고 누락된 날짜를 감지하도록 쿼리를 구성하는 방법
  • Java를 사용하여 누락된 값을 채우는 방법

소개

일정 기간의 데이터를 비교하면 맥락 정보를 얻을 수 있습니다. 예를 들어 특정 웹사이트에서 100만 달러의 수익이 발생했다고 해서 그다지 의미 있는 것은 아닙니다. 하지만 웹사이트를 통해 전 분기 대비 또는 전년 대비 수익이 10배 증가한 점은 실로 놀랍습니다. Google 애널리틱스 API에서는 ga:date, ga:day, ga:month 측정기준을 사용하여 시간 경과에 따른 데이터를 쉽게 표시할 수 있습니다.

쿼리에서 날짜 측정기준만 사용하는 경우, 해당 기간에 포함된 날짜에서 데이터가 0개인 경우 Google 애널리틱스 API는 측정항목의 날짜 및 0 값을 백필합니다.

ga:datega:sessions
2010-03-01101
2010-03-020
2010-03-0369

그러나 다른 측정기준과 함께 날짜를 쿼리하면 복잡해집니다. 날짜 중 하나에 데이터가 없으면 API는 해당 날짜의 항목을 반환하지 않습니다. 데이터가 포함된 이용 가능한 다음 날짜로 건너뜁니다.

ga:keywordga:datega:sessions
의자2010-03-0155
의자2010-03-0348

이상적으로 분석가는 특정 키워드의 누락된 날짜를 위의 첫 번째 예시와 같이 입력하는 것이 좋습니다.

이 문서에서는 실질적으로 데이터를 백필하기 위한 몇 가지 권장사항을 설명합니다.

배경

먼저 이 문제가 존재하는 이유를 살펴보겠습니다. 여기에는 두 가지 이유가 있습니다.

  1. Google 애널리틱스는 수집된 데이터만 처리합니다. 특정 날짜에 사이트를 방문하지 않은 경우 처리할 데이터가 없으므로 데이터가 반환되지 않습니다.
  2. 데이터가 없는 날짜에 사용할 추가 측정기준 수와 값을 결정하기는 매우 어렵습니다.

따라서 Google 애널리틱스 API는 모든 측정기준을 포괄하는 하나의 프로세스를 정의하려고 하지 않고 여러 측정기준이 포함된 쿼리의 데이터를 개발자가 직접 채우는 과정을 거칩니다. 감사합니다. :)

프로그램 개요

다음은 위 차트의 데이터를 백필하는 단계입니다.

  1. 측정기준이 상황에 따라 정렬되도록 쿼리를 수정합니다.
  2. 기간 중 예상 날짜를 결정합니다.
  3. 누락된 날짜를 반복하고 백필합니다.
  4. 나머지 누락된 값을 입력합니다.

쿼리 수정

날짜를 백필하려면 API에서 반환된 데이터가 날짜 누락을 쉽게 감지할 수 있는 형식으로 되어 있는지 확인해야 합니다. 다음은 3월의 처음 5일 동안 ga:keywordga:date를 모두 검색하는 쿼리의 예시입니다.

DataQuery dataQuery = new DataQuery(new URL(BASE_URL));
dataQuery.setIds(TABLE_ID);
dataQuery.setStartDate("2010-03-01");
dataQuery.setEndDate("2010-03-05");
dataQuery.setDimensions("ga:keyword,ga:date");
dataQuery.setMetrics("ga:entrances");

쿼리가 API로 전송되면 결과에 DataEntry 객체 목록이 포함됩니다. 각 항목 객체는 데이터 행을 나타내며 측정기준/측정항목의 이름과 값을 포함합니다. 정렬 매개변수가 사용되지 않았으므로 결과는 임의의 순서로 반환됩니다.

ga:keywordga:datega:entrances
의자2010-03-0414
의자2010-03-0123
테이블2010-03-0418
테이블2010-03-0224
의자2010-03-0313

누락된 날짜를 쉽게 식별하려면 먼저 모든 측정기준을 함께 그룹화해야 합니다. 쿼리의 정렬 매개변수를 원래 쿼리에서 사용된 측정기준으로 설정하면 됩니다.

dataQuery.setSort("ga:keyword,ga:date");

정렬 매개변수를 추가하면 API가 원하는 순서로 결과를 반환합니다.

ga:keywordga:datega:entrances
의자2010-03-0123
의자2010-03-0313
의자2010-03-0414
테이블2010-03-0224
테이블2010-03-0418

두 번째 단계는 모든 측정기준에 대해 모든 날짜가 오름차순으로 반환되는지 확인하는 것입니다. Google 애널리틱스 API는 여러 날짜 측정기준을 제공하지만 ga:date만 날짜 경계 (예: 일, 월, 연도)로 정확하게 정렬할 수 있습니다. 따라서 날짜를 백필하려면 쿼리가 측정기준과 정렬 쿼리 매개변수 모두에서 ga:date 측정기준을 사용해야 합니다.

정렬된 쿼리가 실행되면 동일한 모든 방문 페이지가 나란히 반환되며 날짜는 순차적으로 표시됩니다. 단일 방문 페이지의 날짜 목록은 시계열로 간주할 수 있으며, 날짜 목록이 순서대로 표시되므로 누락된 날짜를 찾기가 훨씬 쉽습니다.

예상 날짜 결정

누락된 날짜를 감지하려면 API에서 반환된 실제 날짜를 모든 시계열의 예상 날짜와 비교해야 합니다. 다음을 통해 예상되는 결과를 파악할 수 있습니다.

  1. API 쿼리에서 예상 시작일 확인
  2. 쿼리 기간의 예상 일수를 계산합니다.

두 값을 함께 사용하면 기간의 각 날짜에 대해 시작일을 1씩 증가시켜 각각의 예상 날짜를 결정할 수 있습니다.

예상 시작일 결정

start-date 쿼리 매개변수를 시리즈의 예상 시작일로 사용할 수 있습니다. API 응답 yyyyMMdd에 반환된 날짜 형식은 쿼리 매개변수 yyyy-MM-dd의 형식과 다르기 때문에 먼저 날짜 형식을 변환해야 사용할 수 있습니다.

setExpectedStartDate 메서드는 날짜 형식을 변환합니다.

  private static SimpleDateFormat queryDateFormat = new SimpleDateFormat("yyyy-MM-dd");
  private static SimpleDateFormat resultDateFormat = new SimpleDateFormat("yyyyMMdd");

  public void setExpectedStartDate(String startDate) {
    try {
      calendar.setTime(queryDateFormat.parse(startDate));
      expectedStartDate = resultDateFormat.format(calendar.getTime());
    } catch (ParseException e) {
      handleException(e);
    }
  }

예상 일수 계산

날짜 범위의 일 수를 가져오기 위해 프로그램은 시작일과 종료일을 Java Date 객체로 파싱합니다. 그런 다음 Calendar 객체를 사용하여 두 날짜 사이의 시간을 파악합니다. 날짜 차이에 하루가 더해져 일수가 포함됩니다.

  private static final long millisInDay = 24 * 60 * 60 * 1000;

  public void setNumberOfDays(DataQuery dataQuery) {
    long startDay = 0;
    long endDay = 0;

    try {
      calendar.setTime(queryDateFormat.parse(dataQuery.getStartDate()));
      startDay = calendar.getTimeInMillis() / millisInDay;

      calendar.setTime(queryDateFormat.parse(dataQuery.getEndDate()));
      endDay = calendar.getTimeInMillis() / millisInDay;
    } catch (ParseException e) {
      handleException(e);
    }

    numberOfDays = (int) (endDay - startDay + 1);
  }

이제 누락된 날짜를 찾는 데 필요한 모든 데이터를 갖추었습니다.

결과에서 각 시계열 식별

쿼리가 실행되면 프로그램은 API 응답의 각 DataEntry 객체를 살펴봅니다. 쿼리가 처음에 정렬되었으므로 응답에는 각 키워드에 대한 부분 시계열이 포함됩니다. 따라서 각 시계열의 시작 부분을 찾은 다음 각 날짜를 살펴보고 API에서 반환하지 않은 누락된 데이터를 채워야 합니다.

이 프로그램은 dimensionValuetmpDimensionValue 변수를 사용하여 각 계열의 시작을 감지합니다.

다음은 응답을 처리하는 전체 코드입니다. 누락된 데이터 채우기는 아래에서 설명합니다.

public void printBackfilledResults(DataFeed dataFeed) {
  String expectedDate = "";
  String dimensionValue = "";
  List<Integer> row = null;

  for (DataEntry entry : dataFeed.getEntries()) {
    String tmpDimValue = entry.getDimensions().get(0).getValue();

    // Detect beginning of a series.
    if (!tmpDimValue.equals(dimensionValue)) {
      if (row != null) {
        forwardFillRow(row);
        printRow(dimensionValue, row);
      }

      // Create a new row.
      row = new ArrayList<Integer>(numberOfDays);
      dimensionValue = tmpDimValue;
      expectedDate = expectedStartDate;
    }

    // Backfill row.
    String foundDate = entry.getDimension("ga:date").getValue();
    if (!foundDate.equals(expectedDate)) {
      backFillRow(expectedDate, foundDate, row);
    }

    // Handle the data.
    Metric metric = entry.getMetrics().get(0);
    row.add(new Integer(metric.getValue()));
    expectedDate = getNextDate(foundDate);
  }

  // Handle the last row.
  if (row != null) {
    forwardFillRow(row);
    printRow(dimensionValue, row);
  }
}

누락된 날짜 백필

일련의 각 항목에 대해 프로그램은 row라는 ArrayList에 측정항목 값 (진입수)을 저장합니다. 새 시계열이 감지되면 새 행이 생성되고 예상 날짜가 예상 시작일로 설정됩니다.

그런 다음 각 항목에서 프로그램은 항목의 날짜 값이 예상 날짜와 동일한지 확인합니다. 두 측정항목이 동일하면 항목의 측정항목이 행에 추가됩니다. 그렇지 않은 경우 프로그램에서 백업해야 할 누락된 날짜를 감지한 것입니다.

backfillRow 메서드는 데이터 백필을 처리합니다. 예상 날짜와 발견된 날짜 및 현재 행을 매개변수로 허용합니다. 그런 다음 두 날짜 사이의 일 수를 결정하고(해당 값은 포함되지 않음) 해당 많은 0을 행에 추가합니다.

  public void backFillRow(String startDate, String endDate, List<Integer> row) {
    long d1 = 0;
    long d2 = 0;

    try {
      calendar.setTime(resultDateFormat.parse(startDate));
      d1 = calendar.getTimeInMillis() / millisInDay;

      calendar.setTime(resultDateFormat.parse(endDate));
      d2 = calendar.getTimeInMillis() / millisInDay;

    } catch (ParseException e) {
      handleException(e);
    }

    long differenceInDays = d2 - d1;
    if (differenceInDays > 0) {
      for (int i = 0; i < differenceInDays; i++) {
        row.add(0);
      }
    }
  }

메서드가 완료되면 행이 데이터로 백필되고 현재 데이터를 추가할 수 있습니다. 그런 다음 getNextDate 메서드를 사용하여 예상 날짜가 발견된 날짜로부터 1일 후로 증가합니다.

public String getNextDate(String initialDate) {
  try {
    calendar.setTime(resultDateFormat.parse(initialDate));
    calendar.add(Calendar.DATE, 1);
    return resultDateFormat.format(calendar.getTime());

  } catch (ParseException e) {
    handleException(e);
  }
  return "";
}

나머지 값 입력

계열 데이터가 row로 처리되었으면 계열이 끝날 때 더 이상 누락된 날짜가 없는지 확인해야 합니다.

forwardFillRow 메서드는 단순히 원래 쿼리의 일수와 행의 현재 크기 간 차이를 계산하고 이 수의 0을 행 끝에 추가합니다.

public void forwardFillRow(List<Integer> row) {
  int remainingElements = numberOfDays - row.size();
  if (remainingElements > 0) {
    for (int i = 0; i < remainingElements; i++) {
      row.add(0);
    }
  }
}

이제 프로그램이 시계열에 누락된 값을 채웠습니다. 이제 모든 데이터가 있으므로 프로그램에서 측정기준 및 측정항목 값을 쉼표로 구분된 목록으로 출력합니다.

결론

이 샘플을 사용하면 API에서 반환하지 않은 날짜의 데이터를 쉽게 백필할 수 있습니다. 위에서 언급했듯이 이 솔루션은 모든 프로그래밍 언어에 맞게 조정할 수 있습니다. 개발자는 이러한 기법을 조정하고 적용하여 여러 측정기준과 측정항목을 처리할 수도 있습니다. 이제 Google 애널리틱스 API에서 반환하는 시계열에 대한 고급 분석을 그 어느 때보다 쉽게 시작할 수 있습니다.