/// <summary> /// Convert one user's preference relations into position matrix /// See: Brun, A., Hamad, A., Buffet, O., & Boyer, A. (2010). /// Towards preference relations in recommender systems. Workshop in ECML-PKDD. /// </summary> /// <param name="userPreferences"></param> /// <returns></returns> public static Vector<double> PreferencesToPositions(SparseMatrix userPreferences) { // Count for each preference type // Actually the original paper count the strict preferred and less preferred by exact match // but here we just compare with the EquallyPreferred, because the preferences can be // scalar with Bradley-Terry model. However, it won't affect the result when it is discrete preference relations. SparseVector preferredCountByItem = SparseVector.OfEnumerable(userPreferences.FoldByRow((count, pref) => count + (pref == Config.Preferences.Preferred ? 1 : 0), 0.0)); SparseVector lessPreferredCountByItem = SparseVector.OfEnumerable(userPreferences.FoldByRow((count, pref) => count + (pref == Config.Preferences.LessPreferred ? 1 : 0), 0.0)); // Note that we assume the diagonal are left empty // otherwise the equally count needs to be offset by 1 for each, i.e. item itself does not count Debug.Assert(userPreferences.Trace() == 0); SparseVector equallyPreferredCountByItem = SparseVector.OfEnumerable(userPreferences.FoldByRow((count, pref) => count + (pref == Config.Preferences.EquallyPreferred ? 1 : 0), 0.0)); // Note that if the position is value zero then it won't appear in positionByItem // because the use of SparseVector.OfVector() will ignore all zero values Vector<double> positionByItem = (preferredCountByItem - lessPreferredCountByItem) .PointwiseDivide(lessPreferredCountByItem + preferredCountByItem + equallyPreferredCountByItem) + Config.Preferences.PositionShift; Vector<double> indicatorOfSeenItems = userPreferences.RowSums(); // If zero, then this item has never been seen for(int i = 0; i < indicatorOfSeenItems.Count; i++) { if(indicatorOfSeenItems[i] == 0) { positionByItem[i] = SparseVector.Zero; } } // Note that the positions do not add up to 0 // because we shifted all values Debug.Assert( Math.Abs(positionByItem.Sum()- ((SparseVector)positionByItem).NonZerosCount * Config.Preferences.PositionShift) < 0.001); //+Config.Preferences.PositionShift; // TODO: May improve later. Some items have position 0 and we dont want to mix // up the position 0 and the 0 in sparsematrix. // So we use a constant to hold the space for position value 0 // it should be reverted back when use //Vector<double> indicatorVector = userPreferences.RowSums(); //for (int i = 0; i < indicatorVector.Count; i++) //{ //if (indicatorVector[i] != 0 && positionByItem[i] != 0) //{ // Debug.Assert(true, "By using the PositionShift constant, we should not be in here."); // positionByItem[i] = Config.ZeroInSparseMatrix; // } //} return positionByItem; }