2011-03-29 68 views
1

我有一個非常簡單的數據集Ruby是否有數據庫或數據結構來實現關係矩陣?

  • 產品
  • 屬性
  • 交叉表:屬性的產品

        red blue modern old fashion (50+ Entries) 
        Jeans myway 0% 100% 30%  30% 
        Polo Shirt 100%  0% 10%  40% 
        (500+ Entries) 
    

在應用程序相關您選擇像紅色,藍色等屬性,並獲得按相關性排序的產品。

什麼是存儲數據的最佳方式,是否有一個好的數據結構(庫)的紅寶石?

(不要告訴我如何與傳統的三代表的SQL和活動記錄,以實現它,我已經知道我正在尋找一個更好的解決方案。)

的一種方法是用一個哈希:

"red" => {"Jeans myway" => 0, "Polo Shirt" => 100}, "blue" => {.. 

這是一個好方法,我應該如何將它存儲到文件?

更好的解決方案:如果我使用關係數據庫,我必須將矩陣拆分爲3個表格產品,屬性,attributes_products。我想將它存儲在一個表/矩陣中,並像矩陣一樣搜索/使用它。

E.g.我想選擇屬性「舊時尚」,「現代」相關(> 0)按相關性排序的產品將返回「牛仔褲myway 0.09」,「Polo Shirt 0.04」。 (相關性通過乘法計算。)

+1

你說你正在尋找一個比SQL + AR更好的解決方案。你能解釋一下你的「更好」標準嗎?是MySQL/PostgreSQL RDBMS的問題,並會使用基於文件的SQLite數據庫或內存數據庫更好?你是否需要隨時間更新關係,或者加載一次並閱讀它們?你是否只需要執行你描述的查詢(選擇一個屬性並查看按相關性排序的產品)? – Phrogz 2011-03-29 20:41:50

+0

我在尋找更像矩陣的解決方案。看我的編輯。 – Beffa 2011-03-29 21:19:45

回答

2

下面是針對您的需求量身定製的解決方案。它允許您選擇任意數量的產品或屬性,並查看按權重乘積排序的另一個軸中的值。它可以讓你以CSV格式存儲你的數據,所以你可以保留一個大的Excel文件以供將來調整。

merge_by,sort_byfilter_by方法允許您在獲得結果時指定要應用的塊。

TESTDATA = <<ENDCSV 
,red,blue,modern,old fashion,sexy 
Jeans myway,0%,100%,30%,30%,70% 
Polo Shirt,100%,0%,10%,40%,1% 
Bra,100%,0%,100%,0%,100% 
ENDCSV 

def test 
    products = RelationTable.load_from_csv(TESTDATA) 

    p products.find(:col, 'old fashion','modern') 
    #=> [["Jeans myway", 9.0], ["Polo Shirt", 4.0]] 

    p products.find(:row, 'Polo Shirt') 
    #=> [["red", 100.0], ["old fashion", 40.0], ["modern", 10.0]] 

    p products.find(:col, 'sexy') 
    #=> [["Bra", 100.0], ["Jeans myway", 70.0], ["Polo Shirt", 1.0]] 

    p products.find(:row, 'Polo Shirt','Bra') 
    #=> [["red", 100.0], ["modern", 10.0]] 

    p products.find(:col, 'sexy','modern') 
    #=> [["Bra", 100.0], ["Jeans myway", 21.0], ["Polo Shirt", 0.1]] 

    p products.find(:col, 'red', 'blue') 
    #=> [] 

    p products.find(:col, 'bogus') 
    #=> [] 
end 

class RelationTable 
    def self.load_from_csv(csv) 
    require 'csv' 
    data = CSV.parse(csv) 
    self.new(data.shift[1..-1], data.map{ |r| r.shift }, data) 
    end 
    def initialize(col_names=[], row_names=[], weights=[]) 
    @by_col = Hash.new{|h,k|h[k]=Hash.new(0)} 
    @by_row = Hash.new{|h,k|h[k]=Hash.new(0)} 
    row_names.each_with_index do |row,r| 
     col_names.each_with_index do |col,c| 
     @by_col[col][row] = @by_row[row][col] = weights[r][c].to_f 
     end 
    end 
    # Multiply all weights, sort by weight (descending), only include non-zero 
    merge_by{ |values| values.inject(1.0){ |weight,v| weight*v/100 }*100 } 
    sort_by{ |key,value| [-value,key] } 
    filter_by{ |key,value| value > 0 } 
    end 
    def merge_by(&proc); @merge = proc; end 
    def sort_by(&proc); @sort = proc; end 
    def filter_by(&proc); @filter = proc; end 
    def find(row_or_col, *names) 
    axis = (row_or_col == :row) ? @by_row : @by_col 
    merge(axis.values_at(*names)).select(&@filter).sort_by(&@sort) 
    end 
    private 
    # Turn an array of hashes into a hash of arrays of values, 
    # and then merge the values using the merge_by proc 
    def merge(hashes) 
     if hashes.length==1 
     hashes.first # Speed optimization; ignores the merge_by block 
     else 
     result = Hash.new{|h,k|h[k]=[]} 
     hashes.each{ |h| h.each{ |k,v| result[k] << v } } 
     result.each{ |k,values| result[k] = @merge[values] } 
     result 
     end 
    end 
end 

test if __FILE__==$0 
+0

感謝您的工作!我現在應該將您的版權包含在我的代碼中。 – Beffa 2011-03-30 06:42:23