Advertisement
sa2304

TestTfIdfCalculation refactored

Nov 19th, 2020
1,056
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.61 KB | None | 0 0
  1. //------------------------------------------------------------------------------
  2. /** Объединяет несколько строк в одну, разделяя их символом separator */
  3. void JoinString(const vector<string> &chnks, char separator, string &result) {
  4.   result.clear();
  5.  
  6.   for (vector<string>::const_iterator p = chnks.begin();
  7.        p != chnks.end(); ++p) {
  8.     result += *p;
  9.     if (p != chnks.end() - 1)
  10.       result += separator;
  11.   }
  12. }
  13.  
  14. //------------------------------------------------------------------------------
  15. /** Повторяет слово count раз, разделяя повторения символом separator */
  16. void RepeatWordWithSeparator(size_t count, const string &word, char separator, string &result) {
  17.   result.clear();
  18.  
  19.   bool first = true;
  20.   for (size_t i = 0; i < count; ++i) {
  21.     if (!first) {
  22.       result += separator;
  23.     }
  24.     first = false;
  25.  
  26.     result += word;
  27.   }
  28. }
  29.  
  30. //------------------------------------------------------------------------------
  31. /** Создает текст документа из слов с заданным числом повторений */
  32. string GenerateTestDocument(const std::map<string, size_t> words) {
  33.   const char separator = ' ';
  34.   std::vector<std::string> text_parts;
  35.   for (const auto & [term, freq] : words) {
  36.     std::string term_repeated;
  37.     RepeatWordWithSeparator(freq, term, separator, term_repeated);
  38.     text_parts.push_back(term_repeated);
  39.   }
  40.   std::string result;
  41.   JoinString(text_parts, separator, result);
  42.  
  43.   return result;
  44. }
  45.  
  46. //------------------------------------------------------------------------------
  47. double CalcTermFrequency(const std::string & term,
  48.                          const std::map<std::string, size_t> & all_words) {
  49.   double tf = 0.0;
  50.   if (all_words.count(term)) {
  51.     auto sum = [](size_t accum, const std::pair<std::string, size_t> & item) {
  52.       const auto & [_, count] = item;
  53.           return accum + count;
  54.     };
  55.     const size_t total_words =
  56.           std::accumulate(all_words.begin(), all_words.end(), 0, sum);
  57.     const int term_count = all_words.at(term);
  58.     tf = static_cast<double>(term_count) / total_words;
  59.   }
  60.  
  61.   return tf;
  62. }
  63.  
  64. //------------------------------------------------------------------------------
  65. double CalcTermInverseDocumentFrequency(const std::string & term,
  66.           const std::vector<std::map<std::string, size_t>> & documents) {
  67.   auto HasTerm = [&term](const std::map<std::string, size_t> & words) {
  68.     return (0 < words.count(term));
  69.   };
  70.   const size_t documents_with_term_count = std::count_if(documents.begin(),
  71.                                                          documents.end(), HasTerm);
  72.  
  73.   return log(static_cast<double>(documents.size()) / documents_with_term_count);
  74. }
  75.  
  76. //------------------------------------------------------------------------------
  77. /** Тест на расчет релевантности документов запросам по TF-IDF */
  78. void TestTfIdfCalculation() {
  79.   SearchServer server;
  80.   const DocumentStatus status_actual = DocumentStatus::ACTUAL;
  81.   const std::vector<int> ratings = {1,2,3,4,5};
  82.  
  83.   const std::vector<std::map<std::string, size_t>> documents = {
  84.     {
  85.       { "this"s, 1 },
  86.       { "is"s, 1 },
  87.       { "a"s, 2 },
  88.       { "test"s, 1 },
  89.     },
  90.     {
  91.       { "this"s, 1 },
  92.       { "is"s, 1 },
  93.       { "another"s, 2 },
  94.       { "example"s, 3 },
  95.     },
  96.   };
  97.  
  98.   // Сперва добавим все документы
  99.   for (int doc_id = 0; doc_id < documents.size(); ++doc_id) {
  100.     const auto & words = documents.at(doc_id);
  101.     const std::string text = GenerateTestDocument(words);
  102.     server.AddDocument(doc_id, text, status_actual, ratings);
  103.   }
  104.  
  105.   // Обойдем все документы, посчитаем их релевантность по TF-IDF, сравним с той, что возвращает поисковик
  106.   for (int doc_id = 0; doc_id < documents.size(); ++doc_id) {
  107.     const auto & words = documents.at(doc_id);
  108.     for (const auto & [term, count] : words) {
  109.       const double tf = CalcTermFrequency(term, words);
  110.       const double idf = CalcTermInverseDocumentFrequency(term, documents);
  111.       const double expected_relevance = tf * idf;
  112.       const int id = doc_id;
  113.       auto HasId = [id](const Document & doc) {
  114.         return doc.id == id;
  115.       };
  116.       auto search_results = server.FindTopDocuments(term);
  117.       auto iter = std::find_if(search_results.begin(), search_results.end(), HasId);
  118.       ASSERT(iter != search_results.end());
  119.       ASSERT_ALMOST_EQUAL(iter->relevance, expected_relevance, 0.01);
  120.     }
  121.   }
  122. }
  123.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement