Điền các giá trị còn thiếu từ yêu cầu có ngày

Nick Mihailovski, Nhóm Google Analytics API – tháng 10 năm 2009

Bài viết này thảo luận cách phát hiện và bổ sung các giá trị chuỗi thời gian bị thiếu trong dữ liệu được trả về từ API Xuất dữ liệu của Google Analytics.


Trước khi bắt đầu

Bài viết giả định rằng bạn biết cách hoạt động của API Xuất dữ liệu của Google Analytics. Mã mẫu nằm trong Java nhưng bạn có thể sử dụng các khái niệm bằng ngôn ngữ mà bạn chọn. Mã cho bài viết này được cung cấp dưới dạng nguồn mở và có thể tải xuống từ dịch vụ lưu trữ dự án.

Sau khi đọc bài viết này, bạn sẽ tìm hiểu:

  • Cách API Xuất dữ liệu của Google Analytics xử lý các phương diện ngày.
  • Cách định cấu trúc truy vấn để nhóm các kết quả và phát hiện ngày bị thiếu.
  • Cách điền các giá trị còn thiếu bằng Java.

Giới thiệu

Thông tin so sánh dữ liệu trong một khoảng thời gian sẽ giúp bạn hiểu rõ hơn. Ví dụ: Việc tuyên bố một trang web tạo ra doanh thu 1 triệu USD không có ý nghĩa nhiều. Tuy nhiên, việc nói rằng doanh thu của một trang web đã tăng gấp 10 lần so với cùng kỳ quý trước hoặc mỗi năm thì thực sự rất ấn tượng. Với API Google Analytics, bạn có thể dễ dàng lập biểu đồ dữ liệu theo thời gian bằng cách sử dụng các phương diện ga:date, ga:dayga:month.

Nếu truy vấn của bạn chỉ sử dụng phương diện ngày, thì nếu có bất kỳ ngày nào trong phạm vi ngày không thu thập dữ liệu, thì API Google Analytics sẽ bổ sung dữ liệu ngày và giá trị 0 cho các chỉ số.

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

Tuy nhiên, sẽ trở nên phức tạp nếu bạn truy vấn ngày cùng với các phương diện khác. Nếu một trong các ngày không có dữ liệu, API sẽ KHÔNG trả về một mục cho ngày đó. Chiến dịch này sẽ chỉ chuyển đến ngày tiếp theo có chứa dữ liệu.

ga:keywordga:datega:sessions
cái ghế2010-03-0155
cái ghế2010-03-0348

Lý tưởng nhất là các nhà phân tích muốn điền ngày tháng còn thiếu cho một từ khóa cụ thể, chẳng hạn như ví dụ đầu tiên ở trên

Bài viết này mô tả một số phương pháp hay nhất để bổ sung dữ liệu trên thực tế.

Thông tin khái quát

Trước tiên, hãy xem lý do xảy ra vấn đề này. Có 2 lý do.

  1. Google Analytics chỉ xử lý dữ liệu được thu thập. Nếu không có ai truy cập vào trang web vào một ngày cụ thể, thì sẽ không có dữ liệu để xử lý, vì vậy, sẽ không có dữ liệu nào được trả về.
  2. Bạn rất khó để xác định có bao nhiêu phương diện bổ sung và giá trị nào sẽ được sử dụng cho những ngày không có dữ liệu.

Vì vậy, thay vì cố gắng xác định một quy trình để quy tắc tất cả, API Google Analytics sẽ để nhà phát triển điền dữ liệu vào các truy vấn có nhiều phương diện. Bạn thật may mắn :)

Tổng quan về chương trình

Dưới đây là các bước để bổ sung dữ liệu trong biểu đồ trên.

  1. Sửa đổi truy vấn để đảm bảo các thứ nguyên được sắp xếp có thể.
  2. Xác định ngày dự kiến từ phạm vi ngày.
  3. Lặp lại và bổ sung những ngày còn thiếu.
  4. Điền mọi giá trị còn thiếu còn lại.

Sửa đổi truy vấn

Để bổ sung ngày, chúng tôi cần đảm bảo dữ liệu trả về từ API ở định dạng giúp dễ dàng phát hiện thiếu ngày. Dưới đây là một truy vấn mẫu để truy xuất cả ga:keywordga:date trong 5 ngày đầu tiên của tháng 3:

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");

Sau khi truy vấn được gửi đến API, kết quả sẽ chứa một danh sách các đối tượng DataEntry. Mỗi đối tượng mục nhập đại diện cho một hàng dữ liệu và bao gồm tên cũng như giá trị cho các phương diện/chỉ số. Vì không có tham số sắp xếp nào được sử dụng nên các kết quả được trả về theo thứ tự tuỳ ý.

ga:keywordga:datega:entrances
cái ghế2010-03-0414
cái ghế2010-03-0123
bàn2010-03-0418
bàn2010-03-0224
cái ghế2010-03-0313

Để dễ dàng xác định ngày nào bị thiếu, trước tiên, chúng tôi cần nhóm tất cả phương diện lại với nhau. Bạn có thể thực hiện việc này bằng cách đặt tham số sắp xếp của truy vấn thành các phương diện dùng trong truy vấn ban đầu.

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

Việc thêm tham số sắp xếp sẽ khiến API trả về kết quả theo thứ tự mong muốn.

ga:keywordga:datega:entrances
cái ghế2010-03-0123
cái ghế2010-03-0313
cái ghế2010-03-0414
bàn2010-03-0224
bàn2010-03-0418

Bước thứ hai là đảm bảo rằng đối với mọi chiều, tất cả ngày đều được trả về theo thứ tự tăng dần. Mặc dù API Google Analytics cung cấp một số phương diện ngày, nhưng chỉ có thể sắp xếp ga:date chính xác theo ranh giới ngày (tức là ngày, tháng, năm). Vì vậy, nếu bạn muốn thay đổi ngày, hãy đảm bảo truy vấn của bạn sử dụng phương diện ga:date trong cả phương diện và sắp xếp tham số truy vấn.

Sau khi thực thi truy vấn được sắp xếp, tất cả các trang đích giống nhau sẽ được trả về cạnh nhau và ngày sẽ theo thứ tự tuần tự. Danh sách ngày cho một trang đích có thể được coi là một chuỗi thời gian và vì chúng được sắp xếp theo thứ tự, nên việc xác định ngày bị thiếu sẽ dễ dàng hơn nhiều.

Xác định ngày dự kiến

Để phát hiện ngày bị thiếu, chúng ta cần so sánh ngày thực tế mà API trả về với ngày dự kiến trong mỗi chuỗi thời gian. Chúng ta có thể tìm ra những gì được mong đợi bằng cách:

  1. Xác định ngày bắt đầu dự kiến từ truy vấn API.
  2. Đếm số ngày dự kiến trong phạm vi ngày của truy vấn.

Bạn có thể sử dụng cả hai giá trị này cùng nhau để xác định từng ngày dự kiến bằng cách tăng ngày bắt đầu thêm 1 cho mỗi ngày trong phạm vi ngày.

Xác định ngày bắt đầu dự kiến

Chúng ta có thể sử dụng tham số truy vấn start-date làm ngày bắt đầu dự kiến của bộ sách. Vì định dạng ngày được trả về trong phản hồi API yyyyMMdd khác với định dạng của tham số truy vấn yyyy-MM-dd nên trước tiên, chúng ta cần chuyển đổi định dạng ngày trước khi có thể sử dụng định dạng đó.

Phương thức setExpectedStartDate chuyển đổi định dạng của ngày tháng.

  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);
    }
  }

Đếm số ngày dự kiến

Để biết số ngày trong phạm vi ngày, chương trình sẽ phân tích cú pháp ngày bắt đầu và ngày kết thúc thành các đối tượng Date trong Java. Sau đó, sử dụng đối tượng Calendar để tìm ra thời gian giữa cả hai ngày. Sẽ có thêm một ngày vào mức chênh lệch về ngày để đếm số lượng.

  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);
  }

Bây giờ, chúng ta đã có tất cả dữ liệu cần thiết để xác định những ngày còn thiếu.

Xác định từng chuỗi thời gian trong kết quả

Sau khi truy vấn được thực thi, chương trình sẽ xem xét từng đối tượng DataEntry trong phản hồi của API. Vì cụm từ tìm kiếm được sắp xếp ban đầu nên phản hồi sẽ có một chuỗi thời gian riêng cho mỗi từ khoá. Vì vậy, chúng ta cần tìm điểm bắt đầu của từng chuỗi thời gian, sau đó xem từng ngày và điền vào dữ liệu bị thiếu mà API không trả về.

Chương trình này sử dụng các biến dimensionValuetmpDimensionValue để phát hiện điểm bắt đầu của từng chuỗi.

Dưới đây là toàn bộ mã để xử lý phản hồi. Việc điền dữ liệu còn thiếu sẽ được thảo luận bên dưới.

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);
  }
}

Bổ sung những ngày bị thiếu

Đối với mỗi mục nhập trong một chuỗi, chương trình sẽ lưu trữ các giá trị chỉ số (số lượt truy cập) trong một ArrayList có tên là row. Khi phát hiện một chuỗi thời gian mới, hệ thống sẽ tạo một hàng mới và đặt ngày dự kiến thành ngày bắt đầu dự kiến.

Sau đó, đối với mỗi mục nhập, chương trình sẽ kiểm tra xem giá trị ngày trong mục nhập có bằng với ngày dự kiến hay không. Nếu chúng bằng nhau thì chỉ số trong mục nhập sẽ được thêm vào hàng. Nếu không, chương trình đã phát hiện thấy những ngày bị thiếu và cần bổ sung.

Phương thức backfillRow xử lý dữ liệu bổ sung bị thay thế. Phương thức này chấp nhận dưới dạng các thông số ngày dự kiến và ngày tìm thấy cũng như hàng hiện tại. Sau đó, hàm này xác định số ngày giữa hai ngày (không bao gồm) và thêm nhiều giá trị 0 vào hàng.

  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);
      }
    }
  }

Khi phương thức này hoàn tất, hàng đó đã được bổ sung dữ liệu và có thể thêm dữ liệu hiện tại. Sau đó, ngày dự kiến sẽ tăng lên một ngày sau ngày tìm thấy bằng cách sử dụng phương thức getNextDate.

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 "";
}

Điền mọi giá trị còn lại

Sau khi xử lý dữ liệu về bộ sách thành row, chúng tôi phải kiểm tra để đảm bảo rằng không còn ngày nào bị thiếu ở cuối bộ sách.

Phương thức forwardFillRow chỉ đơn giản là tính toán mức chênh lệch giữa số ngày trong truy vấn ban đầu với kích thước hiện tại của hàng và thêm nhiều giá trị 0 đó vào cuối hàng.

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

Đến đây, chương trình đã điền mọi giá trị còn thiếu trong chuỗi thời gian. Bây giờ, chúng ta đã có tất cả dữ liệu, chương trình này sẽ in các giá trị phương diện và chỉ số dưới dạng danh sách được phân tách bằng dấu phẩy.

Kết luận

Bằng cách sử dụng mẫu này, bạn có thể dễ dàng bổ sung dữ liệu cũ vào những ngày mà API không trả về. Như đã đề cập ở trên, giải pháp này có thể điều chỉnh cho phù hợp với mọi ngôn ngữ lập trình. Các nhà phát triển thậm chí có thể điều chỉnh và áp dụng những kỹ thuật này để xử lý nhiều phương diện và nhiều chỉ số. Giờ đây, bạn có thể bắt đầu thực hiện việc phân tích nâng cao về những chuỗi thời gian do API Google Analytics trả về thậm chí dễ dàng hơn bao giờ hết.