2

私はオブジェクトのベクトルを持っています(オブジェクトは、他のフィールドの中で文字列という用語を持つ文字列フィールドを含む用語ノードです)

class TermNode {
private:
    std::wstring term;
    double weight;
    ...
public:
    ...
};

いくつかの処理とスコアの計算の後、これらのオブジェクトは最終的に次のような TermNode ポインターのベクトルに格納されます。

std::vector<TermNode *> termlist;

最大 400 エントリを含むこのベクトルの結果リストは、次のようになります。

DEBUG: 'knowledge' term weight=13.5921
DEBUG: 'discovery' term weight=12.3437
DEBUG: 'applications' term weight=11.9476
DEBUG: 'process' term weight=11.4553
DEBUG: 'knowledge discovery' term weight=11.4509
DEBUG: 'information' term weight=10.952
DEBUG: 'techniques' term weight=10.4139
DEBUG: 'web' term weight=10.3733
...

私がやろうとしているのは、用語リスト内のフレーズにも含まれる部分文字列の最終リストをクリーンアップすることです。たとえば、上記のリストのスニペットを見ると、「知識の発見」というフレーズがあります。したがって、 「知識」「発見」という単一の用語を削除したいと思います。これらもリストに含まれており、このコンテキストでは冗長であるためです。単一の用語を含むフレーズを保持したい。また、3 文字以下のすべての文字列を削除することも考えています。しかし、それは今のところ単なる考えです。

このクリーンアップ プロセスでは、remove_if / find_if (新しい C++ ラムダを使用) を使用してクラスをコーディングしたいと考えており、そのコードをコンパクトなクラスに含めるとよいでしょう。

これを解決する方法がよくわかりません。問題は、おそらく削除マーカーとしてフラグを設定して、削除する文字列を最初に特定する必要があることです。つまり、そのリストを前処理する必要があります。単一の用語と、それらの単一の用語の 1 つを含むフレーズを見つける必要があります。これは簡単な作業ではなく、高度なアルゴリズムが必要になると思います。接尾辞ツリーを使用して部分文字列を識別しますか?

ベクターの別のループと、おそらく同じベクターのコピーがクリーンアップされる可能性があります。時間的に最も効率的なものを探しています。

私はstd::list erase incompatible iterator using the remove_if / find_if とstd::vector? .

したがって、問題は基本的に、これを行い、複数のループを回避するスマートな方法と、削除する単一の用語を特定するにはどうすればよいかということです。私は本当に何かを見逃しているのかもしれませんが、おそらく誰かがそこにいて、私に良いヒントを与えてくれます.

ご感想ありがとうございます!

アップデート

次のように、Scrubbins が推奨する方法で冗長な単一用語の削除を実装しました。

/**
 * Functor gets the term of each TermNode object, looks if term string
 * contains spaces (ie. term is a phrase), splits phrase by spaces and finally
 * stores thes term tokens into a set. Only term higher than a score of 
 * 'skipAtWeight" are taken tinto account.
 */
struct findPhrasesAndSplitIntoTokens {
private:
    set<wstring> tokens;
    double skipAtWeight;

public:
    findPhrasesAndSplitIntoTokens(const double skipAtWeight)
    : skipAtWeight(skipAtWeight) {
    }

    /**
     * Implements operator()
     */
    void operator()(const TermNode * tn) {
        // --- skip all terms lower skipAtWeight
        if (tn->getWeight() < skipAtWeight)
            return;

        // --- get term
        wstring term = tn->getTerm();
        // --- iterate over term, check for spaces (if this term is a phrase)
        for (unsigned int i = 0; i < term.length(); i++) {
            if (isspace(term.at(i))) {
if (0) {
                wcout << "input term=" << term << endl;
}
                // --- simply tokenze term by space and store tokens into 
                // --- the tokens set
                // --- TODO: check if this really is UTF-8 aware, esp. for
                // --- strings containing umlauts, etc  !!
                wistringstream iss(term);
                copy(istream_iterator<wstring,
                        wchar_t, std::char_traits<wchar_t> >(iss),
                    istream_iterator<wstring,
                        wchar_t, std::char_traits<wchar_t> >(),
                    inserter(tokens, tokens.begin()));
if (0) {
                wcout << "size of token set=" << tokens.size() << endl;
                for_each(tokens.begin(), tokens.end(), printSingleToken());
}
            }
        }
    }

    /**
     * return set of extracted tokens
     */
    set<wstring> getTokens() const {
        return tokens;
    }
};

/**
 * Functor to find terms in tokens set
 */
class removeTermIfInPhraseTokensSet {
private:
    set<wstring> tokens;

public:
    removeTermIfInPhraseTokensSet(const set<wstring>& termTokens)
    : tokens(termTokens) {
    }

    /**
     * Implements operator()
     */
    bool operator()(const TermNode * tn) const {
        if (tokens.find(tn->getTerm()) != tokens.end()) {
            return true;
        }
        return false;
    }
};

...

findPhrasesAndSplitIntoTokens objPhraseTokens(6.5);
objPhraseTokens = std::for_each(
    termList.begin(), termList.end(), objPhraseTokens);
set<wstring> tokens = objPhraseTokens.getTokens();
wcout << "size of tokens set=" << tokens.size() << endl;
for_each(tokens.begin(), tokens.end(), printSingleToken());

// --- remove all extracted single tokens from the final terms list
// --- of similar search terms 
removeTermIfInPhraseTokensSet removeTermIfFound(tokens);
termList.erase(
    remove_if(
        termList.begin(), termList.end(), removeTermIfFound),
    termList.end()
);

for (vector<TermNode *>::const_iterator tl_iter = termList.begin();
      tl_iter != termList.end(); tl_iter++) {
    wcout << "DEBUG: '" << (*tl_iter)->getTerm() << "' term weight=" << (*tl_iter)->getNormalizedWeight() << endl;
    if ((*tl_iter)->getNormalizedWeight() <= 6.5) break;
}

...

私のubuntuサーバーには現在g ++ 4.4.1がインストールされているため、C ++ 11ラムダ構文を使用できませんでした。いずれかの方法。それは今のところ仕事をします。進むべき道は、結果として得られた重み付けされた用語の品質を他の検索結果セットでチェックし、品質を改善する方法を確認し、元のクエリ用語と組み合わせてより関連性の高い用語を高める方法を見つけることです. 簡単な作業ではないかもしれませんが、「単純なヒューリスティック」があればいいのにと思います。しかし、それはさらに一歩踏み出すと、別の新しい質問になるかもしれません:-)

この豊かな考えの貢献に感謝します!

4

2 に答える 2