2011-03-29 64 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