Skip to content

2015

Beer Recommendation Engine Using PredictionIO

The Problem:

We all love beer. Yet, today we are faced with an unprecedented variety of options, leaving us overwhelmed and indecisive: What should we drink next?

My Solution:

In our Hack Reactor thesis project, I built a machine-learning server using PredictionIO as a beer recommendation engine. Broadly speaking, the app is based on two main strategies:

  1. Content Filtering Approach: This uses various characteristics of each beer to identify its nature. For example, we utilize breweryDB to determine the style of a specific beer, its alcohol by volume (ABV), and its International Bittering Units (IBU). We then query the database to find beers with similar styles.

  2. Collaborative Filtering Approach: This relies solely on past user behavior, specifically your beer ratings. It employs matrix factorization techniques using the Alternating Least Squares (ALS) algorithm. We characterize both beers and users by vectors of factors inferred from beer rating patterns. A high correspondence between beer and user factors leads to a recommendation.

One advantage of using matrix factorization is its capability to incorporate additional information. When explicit feedback (i.e., your ratings) is not available, we can infer user preferences using implicit feedback, such as your browsing history and search patterns.

As a result, OnTapp matches you with beers that suit your taste. To get a beer recommendation and try out our demo, please visit: http://ontappapp.com/.

使用PredictionIO的啤酒推薦引擎

問題:

我們都愛啤酒。然而,今天我們面臨的是前所未有的選擇多樣性,讓我們感到不知所措和猶豫不決:我們下一個該喝什麼?

我的解決方案:

在我們的Hack Reactor論文計畫中,我構建了一個使用PredictionIO作為啤酒推薦引擎的機器學習服務器。廣義地說,該應用基於兩個主要策略:

  1. 內容過濾方法:這使用每種啤酒的各種特性來識別其性質。例如,我們利用breweryDB來確定特定啤酒的風格,其酒精度(ABV)和其國際苦味單位(IBU)。然後我們查詢數據庫以找到風格相似的啤酒。

  2. 協同過濾方法:這完全依賴於過去的用戶行為,特別是你的啤酒評級。它使用交替最小二乘法(ALS)算法的矩陣因子分解技術。我們通過從啤酒評級模式中推斷出來的因子向量來描述啤酒和用戶。啤酒和用戶因子之間的高度相關性將導致推薦。

使用矩陣因子分解的一個優點是其能夠加入附加資訊。當沒有顯性反饋(即,你的評級)時,我們可以使用隱性反饋,如你的瀏覽歷史和搜尋模式,來推斷出用戶偏好。

結果,OnTapp將你和適合你口味的啤酒配對起來。要獲得啤酒推薦並嘗試我們的示例,請訪問:http://ontappapp.com/.

AngularUI: Handling Mouseover Events for Google Maps Markers

The Problem:

It took me a while to figure this out, so I thought the issue deserved its own blog post. I found myself confused when trying to hook up mouseover events with Google Map markers using AngularUI. The documentation was vague on this matter, stating that it is only supported for a single marker and not for multiple markers.

For a single marker, it's straightforward to specify a directive with an events property in the HTML:

<ui-gmap-marker events="marker.events"></ui-gmap-marker>

Then in the controller:

$scope.marker = {
  events: {
    mouseover: function (marker, eventName, args) {
      // Callback code here
    },
  },
}

However, the same approach doesn't work for multiple markers. I initially thought I could simply add mouseover events to each marker like so:

markers.push({
  events: {
    mouseover: function (mapModel, eventName, originalEventArgs) {
      console.log("I'd really like to show the info window on mouseover.")
    },
  },
})

My Solution:

To resolve this, add the following to the HTML markers directive:

events="clickEventsObject"

And in the controller:

$scope.clickEventsObject = {
  mouseover: markerMouseOver,
  mouseout: markerMouseOut,
}

function markerMouseOver(marker, e) {
  // Callback code here
}

function markerMouseOut(marker, e) {
  // Callback code here
}

It works! This handler returns a directly-mapped array of the models that belong to the gMarker cluster sent along with the event. If you have any questions, feel free to ask. 😊

AngularUI:處理Google地圖標記的滑鼠懸停事件

問題:

我花了一些時間才弄清楚這個問題,所以我覺得這個問題值得一篇自己的博客文章。當我試著用AngularUI連接Google Map標記的滑鼠懸停事件時,我發現自己很困惑。文件在這件事情上很含糊,說它只支援單一標記,而不支援多個標記。

對於單一標記,只需要在HTML中指定一個帶有 events 屬性的指令就很直接:

<ui-gmap-marker events="marker.events"></ui-gmap-marker>

然後在控制器中:

$scope.marker = {
  events: {
    mouseover: function (marker, eventName, args) {
      // 回調代碼在此
    },
  },
}

然而,相同的做法不適用於多個標記。我最初以為我可以簡單地為每個標記添加滑鼠懸停事件,就像這樣:

markers.push({
  events: {
    mouseover: function (mapModel, eventName, originalEventArgs) {
      console.log("I'd really like to show the info window on mouseover.")
    },
  },
})

我的解決方案:

為了解決這個問題,將以下內容添加到HTML markers 指令:

events="clickEventsObject"

並在控制器中:

$scope.clickEventsObject = {
  mouseover: markerMouseOver,
  mouseout: markerMouseOut,
}

function markerMouseOver(marker, e) {
  // 回調代碼在此
}

function markerMouseOut(marker, e) {
  // 回調代碼在此
}

它成功了!這個處理程序返回一個直接映射的模型陣列,該陣列屬於與事件一起發送的gMarker集群。如果你有任何問題,請隨時向我提問。😊

Hacking New Year's Resolutions for 2015

As the year draws to a close and a new one begins on January 1st, many people set New Year's resolutions. However, 92% of New Year's resolutions ultimately fail. Why is that?

The question intrigues me. With my hacker mentality, I enjoy applying problem-solving skills to various situations, and I would be ecstatic to break this 8% success rate barrier.

Research suggests that 'willpower' may be a finite resource. We all aspire to improve ourselves, but relying solely on willpower is often insufficient. Failing to meet our goals can be disheartening, leading to a cycle of self-criticism that doesn't help us move forward.

To effect meaningful change, we need to keep our goals both simple and achievable. Tackling low-hanging fruit can provide the motivation to keep going. It's important to convert vague aspirations into specific objectives and to strive for them daily. After all, we are the sum of our repeated actions. Excellence, then, is more a habit than an isolated act.

My Annual Objectives

To keep myself on track, I maintain a daily to-do list that includes:

  1. Always Be Coding – Dedicate at least one hour each day to solving toy problems or taking on coding challenges.

  2. Prioritize Physical Health – Spend 30 minutes a day jogging or engaging in other exercises. A healthy body fosters a healthy mind.

  3. Commit to Learning – Allocate an hour each day to reading books or watching educational videos to expand your knowledge.

  4. Strengthen Social Bonds – Spend an hour each day connecting with family and friends, whether it's over lunch, dinner, or a simple catch-up.

找出2015年新年決心的竅門

當一年即將結束,新的一年在1月1日開始時,許多人會設定新年決心。然而,92%的新年決心最終都會失敗。為什麼會這樣?

這個問題引起了我的興趣。以我的黑客思維,我喜歡將問題解決技巧應用到各種情況中,如果能突破這8%的成功率,我會非常高興。

研究顯示,'意志力'可能是一種有限的資源。我們都渴望改進自己,但僅靠意志力往往不足以達成目標。未能達成我們的目標可能會讓人沮喪,導致一種自我批評的循環,這並不幫助我們前進。

要產生有意義的變化,我們需要保持我們的目標既簡單又可實現。解決手邊易於應對的問題,可以提供繼續前進的動力。將模糊的抱負轉化為具體的目標並每天為之努力是很重要的。畢竟,我們是我們重複行動的總和。因此,卓越更多的是一種習慣,而非孤立的行為。

我的年度目標

為保持自己在正軌上,我制定了每日待辦事項,包括:

  1. 始終保持編程- 每天至少花一個小時解決玩具問題或接受編程挑戰。

  2. 優先考慮身體健康 - 每天花30分鐘慢跑或做其他運動。 健康的身體養育健康的心靈。

  3. 致力於學習- 每天花一個小時閱讀書籍或觀看教育影片,擴展你的知識。

  4. 加強社交網絡- 每天花一個小時和家人或朋友聯繫,無論是在午餐,晚餐,或一次簡單的聊天。

Angular.js Factory: Handling $http Asynchronously

The Problem:

When I was using the $http service to get data from a remote API, the code snippet below was unable to return data back to the controller.

myApp.factory("myFactory", function ($http) {
  var data = { anArray: [] }

  $http.get("/api").success(function (response) {
    data.anArray.push(response)
  })

  return data
})

This issue arises because the return statement is executed before the $http GET request finishes pushing data into the array.

My Solution:

To handle data asynchronously, the controller needs to instruct the service on what actions to take when the data is received later:

myApp.factory("myFactory", function ($http) {
  return {
    getData: function () {
      return $http.get("/api")
    },
  }
})

myApp.controller("MyController", [
  "$scope",
  "myFactory",
  function ($scope, myFactory) {
    $scope.data = []

    var handleSuccess = function (response, status) {
      $scope.data = response
    }

    myFactory.getData().success(handleSuccess)
  },
])

Let me know if you have any questions. 😊

Angular.js 工廠:異步處理 $http

問題:

當我使用 $http 服務從遠程API獲取數據時,下面的代碼片段無法將數據返回到控制器。

myApp.factory("myFactory", function ($http) {
  var data = { anArray: [] }

  $http.get("/api").success(function (response) {
    data.anArray.push(response)
  })

  return data
})

這個問題的出現是因為在 $http GET請求將數據推送到數組之前,就執行了 return 語句。

我的解決方案:

為了異步處理數據,當數據被接收到時,控制器需要指導服務採取什麼行動:

myApp.factory("myFactory", function ($http) {
  return {
    getData: function () {
      return $http.get("/api")
    },
  }
})

myApp.controller("MyController", [
  "$scope",
  "myFactory",
  function ($scope, myFactory) {
    $scope.data = []

    var handleSuccess = function (response, status) {
      $scope.data = response
    }

    myFactory.getData().success(handleSuccess)
  },
])

如果你有任何問題,請讓我知道。😊

Setting Up a Proxy Server with Express

The Problem:

I am working on a project that uses BreweryDB. While trying to load some data from the API, I encountered a problem: the API doesn't support JSONP. This leads to a CORS issue when I attempt to fetch data directly using Angular:

XMLHttpRequest cannot load [https://api.brewerydb.com/v2/.](https://api.brewerydb.com/v2/). No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.

My Solution:

To avoid exposing my API key, I needed to set up an intermediate proxy. Below are the step-by-step instructions for setting up a proxy using Node.js and Express.

Step 1: Install Express and Request

npm install express --save
npm install request --save

Step 2: Create a server.js File

var express = require('express');
var request = require('request');
var app = express();

Step 3: Set Up the Route (replace API_KEY with your actual API key)

app.get('/api', function(req, res) {
  request('https://api.brewerydb.com/v2/?key=' + API_KEY, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      console.log(body);
      res.send(body);
    }
  });
});

Step 4: Set Up the Port

app.listen(3000);
console.log('Server running on port %d', 3000);

Step 5: Start the Server (node server.js)

Open your browser and navigate to http://localhost:3000/api. You should be able to see the JSON object and log it in your browser console:

"message": "Request Successful",
"data": "You have reached the BreweryDB.com API. For access, check out http://www.brewerydb.com/developers",
"status": "success"

If you encounter any problems, feel free to send me an email. ☺

使用Express設置代理伺服器

問題:

我正在進行一個使用BreweryDB的項目。在嘗試從API加載一些數據時,我遇到一個問題:該API不支援JSONP。當我試圖直接使用Angular獲取數據時,會導致CORS問題:

XMLHttpRequest無法加載[https://api.brewerydb.com/v2/.](https://api.brewerydb.com/v2/)。請求的資源上沒有'Access-Control-Allow-Origin'頭。因此,不允許“http://localhost:3000”的來源訪問。

我的解決方案:

為了避免暴露我的API密鑰,我需要設置一個中間代理。以下是使用Node.js和Express設置代理的步驟說明。

步驟1:安裝Express和Request

npm install express --save
npm install request --save

步驟2:建立一個 server.js 檔案

var express = require('express');
var request = require('request');
var app = express();

步驟3:設定路由(將 API_KEY 換成你實際的API密鑰)

app.get('/api', function(req, res) {
  request('https://api.brewerydb.com/v2/?key=' + API_KEY, function (error, response, body) {
    if (!error && response.statusCode === 200) {
      console.log(body);
      res.send(body);
    }
  });
});

步驟4:設定埠口

app.listen(3000);
console.log('伺服器在埠口%d運行', 3000);

步驟5:啟動伺服器(node server.js

開啟你的瀏覽器,導航到 http://localhost:3000/api 。你應該能看到JSON物件並且在你的瀏覽器控制台中將其記錄下來:

"message": "請求成功",
"data": "您已到達BreweryDB.com API。對於訪問,請查看http://www.brewerydb.com/developers",
"status": "成功"

如果您遇到任何問題,請隨時向我發送電子郵件。☺