2016-03-22 2 views
1

Ich versuche, die Ähnlichkeit zwischen Strings zu messen, indem ich Dice's Coefficient (aka Pair Similarity) in BigQuery verwende. Für eine Sekunde dachte ich, dass ich das nur mit Standardfunktionen machen kann.REGEXP_REPLACE Muster muss const sein? Strings in BigQuery vergleichen

Angenommen, ich muss "Gana" und "Gano" vergleichen. Dann würde ich „kochen“ diese beiden Saiten im Voraus, in ‚ga | eine | na‘ und ‚ga | eine | no‘ (Listen von 2-Gramm) und dies tun:

REGEXP_REPLACE('ga|an|na', 'ga|an|no', '') 

dann basierend auf einer Änderung in der Länge Ich kann meinen Koeff berechnen.

Aber einmal auf die Tabelle angewandt, erhalte ich:

REGEXP_REPLACE zweites Argument

Gibt es eine Abhilfe für das const und nicht-null sein muss? Mit dem einfachen REPLACE() zweiten Argument kann ein Feld sein.

Vielleicht gibt es einen besseren Weg, es zu tun? Ich weiß, ich kann stattdessen UDF tun. Aber ich wollte sie hier vermeiden. Wir führen große Aufgaben aus, und UDFs sind im Allgemeinen (zumindest in meiner Erfahrung) langsamer und unterliegen einem unterschiedlichen Grenzwert für den gemeinsamen Zugriff.

Antwort

0

REGEXP_REPLACE zweites Argument muss const sein und nicht null
Gibt es eine Workaround für das?

Unten ist nur eine Idee/Richtung oben Frage Logik angewendet Adresse, die Sie beschrieben:

Ich würde „kochen“, diese beiden Saiten im Voraus in ‚ga | eine | na‘ und ‚ga | an | no '(Listen von 2 Gramm) und tun Sie das: REGEXP_REPLACE (' ga | an | na ', ' ga | an | no ',' '). Dann kann ich basierend auf der Längenänderung meinen Koeff. Berechnen.

"Abhilfe" ist:

SELECT a.w AS w1, b.w AS w2, SUM(a.x = b.x)/COUNT(1) AS c 
FROM (
    SELECT w, SPLIT(p, '|') AS x, ROW_NUMBER() OVER(PARTITION BY w) AS pos 
    FROM 
    (SELECT 'gana' AS w, 'ga|an|na' AS p) 
) AS a 
JOIN (
    SELECT w, SPLIT(p, '|') AS x, ROW_NUMBER() OVER(PARTITION BY w) AS pos 
    FROM 
    (SELECT 'gano' AS w, 'ga|an|no' AS p), 
    (SELECT 'gamo' AS w, 'ga|am|mo' AS p), 
    (SELECT 'kana' AS w, 'ka|an|na' AS p) 
) AS b 
ON a.pos = b.pos 
GROUP BY w1, w2 

Vielleicht gibt einen besseren Weg, es zu tun ist?

unten ist das einfache Beispiel, wie Pair Ähnlichkeit hier angefahren werden kann (einschließlich Gebäuden Bigramme Sätze und die Berechnung des Koeffizienten:

SELECT 
    a.word AS word1, b.word AS word2, 
    2 * SUM(a.bigram = b.bigram)/
    (EXACT_COUNT_DISTINCT(a.bigram) + EXACT_COUNT_DISTINCT(b.bigram)) AS c 
FROM (
    SELECT word, char + next_char AS bigram 
    FROM (
    SELECT word, char, LEAD(char, 1) OVER(PARTITION BY word ORDER BY pos) AS next_char 
    FROM (
     SELECT word, SPLIT(word, '') AS char, ROW_NUMBER() OVER(PARTITION BY word) AS pos 
     FROM 
     (SELECT 'gana' AS word) 
    ) 
) 
    WHERE next_char IS NOT NULL 
    GROUP BY 1, 2 
) a 
CROSS JOIN (
    SELECT word, char + next_char AS bigram 
    FROM (
    SELECT word, char, LEAD(char, 1) OVER(PARTITION BY word ORDER BY pos) AS next_char 
    FROM (
     SELECT word, SPLIT(word, '') AS char, ROW_NUMBER() OVER(PARTITION BY word) AS pos 
     FROM 
     (SELECT 'gano' AS word) 
    ) 
) 
    WHERE next_char IS NOT NULL 
    GROUP BY 1, 2 
) b 
GROUP BY 1, 2 
+0

Wenn eine Antwort hat Ihnen geholfen, Ihr Problem zu lösen und du akzeptierst es, du solltest darüber nachdenken, es zu wählen. Weitere Informationen finden Sie unter http://stackoverflow.com/help/someone-answers und im Abschnitt "Upvote" in http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work#5235 –

1

Sie können JavaScript-Code für BigQuery SQL-Abfragen eingeben.

Ähnlichkeit messen Sie Levenshtein-Distanz mit einer Abfrage wie folgt (von https://stackoverflow.com/a/33443564/132438) verwenden:

SELECT * 
FROM js(
(
    SELECT title,target FROM 
    (SELECT 'hola' title, 'hello' target), (SELECT 'this is beautiful' title, 'that is fantastic' target) 
), 
    title, target, 
    // Output schema. 
    "[{name: 'title', type:'string'}, 
    {name: 'target', type:'string'}, 
    {name: 'distance', type:'integer'}]", 
    // The function 
    "function(r, emit) { 

    var _extend = function(dst) { 
    var sources = Array.prototype.slice.call(arguments, 1); 
    for (var i=0; i<sources.length; ++i) { 
     var src = sources[i]; 
     for (var p in src) { 
     if (src.hasOwnProperty(p)) dst[p] = src[p]; 
     } 
    } 
    return dst; 
    }; 

    var Levenshtein = { 
    /** 
    * Calculate levenshtein distance of the two strings. 
    * 
    * @param str1 String the first string. 
    * @param str2 String the second string. 
    * @return Integer the levenshtein distance (0 and above). 
    */ 
    get: function(str1, str2) { 
     // base cases 
     if (str1 === str2) return 0; 
     if (str1.length === 0) return str2.length; 
     if (str2.length === 0) return str1.length; 

     // two rows 
     var prevRow = new Array(str2.length + 1), 
      curCol, nextCol, i, j, tmp; 

     // initialise previous row 
     for (i=0; i<prevRow.length; ++i) { 
     prevRow[i] = i; 
     } 

     // calculate current row distance from previous row 
     for (i=0; i<str1.length; ++i) { 
     nextCol = i + 1; 

     for (j=0; j<str2.length; ++j) { 
      curCol = nextCol; 

      // substution 
      nextCol = prevRow[j] + ((str1.charAt(i) === str2.charAt(j)) ? 0 : 1); 
      // insertion 
      tmp = curCol + 1; 
      if (nextCol > tmp) { 
      nextCol = tmp; 
      } 
      // deletion 
      tmp = prevRow[j + 1] + 1; 
      if (nextCol > tmp) { 
      nextCol = tmp; 
      } 

      // copy current col value into previous (in preparation for next iteration) 
      prevRow[j] = curCol; 
     } 

     // copy last col value into previous (in preparation for next iteration) 
     prevRow[j] = nextCol; 
     } 

     return nextCol; 
    } 

    }; 

    var the_title; 

    try { 
    the_title = decodeURI(r.title).toLowerCase(); 
    } catch (ex) { 
    the_title = r.title.toLowerCase(); 
    } 

    emit({title: the_title, target: r.target, 
     distance: Levenshtein.get(the_title, r.target)}); 

    }") 
+0

Hallo Felipe, so JavaScript ist die einzige Option?Ich wollte es wegen Geschwindigkeitsbeschränkungen vermeiden. Aber wenn nicht möglich - wird JS verwenden. –

+0

OMG Inline-Javascript ?! Ich hatte gehofft, dass so etwas möglich ist, aber ich konnte nicht herausfinden, wie es geht. Danke für das Posten! –

+0

Können Sie mir sagen, wo Sie die Dokumentation zu dieser JS() - Funktion finden können? Ich habe Probleme, es zu finden, obwohl ich jetzt weiß, dass es existiert. Vielen Dank! –

1

Unten zugeschnitten ist für Ähnlichkeit
verwendet wurde, in How to perform trigram operations in Google BigQuery? und basierend auf https://storage.googleapis.com/thomaspark-sandbox/udf-examples/pataky.js von @thomaspark

SELECT text1, text2, similarity FROM 
JS(
// input table 
(
    SELECT * FROM 
    (SELECT 'mikhail' AS text1, 'mikhail' AS text2), 
    (SELECT 'mikhail' AS text1, 'mike' AS text2), 
    (SELECT 'mikhail' AS text1, 'michael' AS text2), 
    (SELECT 'mikhail' AS text1, 'javier' AS text2), 
    (SELECT 'mikhail' AS text1, 'thomas' AS text2) 
) , 
// input columns 
text1, text2, 
// output schema 
"[{name: 'text1', type:'string'}, 
    {name: 'text2', type:'string'}, 
    {name: 'similarity', type:'float'}] 
", 
// function 
"function(r, emit) { 

    var _extend = function(dst) { 
    var sources = Array.prototype.slice.call(arguments, 1); 
    for (var i=0; i<sources.length; ++i) { 
     var src = sources[i]; 
     for (var p in src) { 
     if (src.hasOwnProperty(p)) dst[p] = src[p]; 
     } 
    } 
    return dst; 
    }; 

    var Levenshtein = { 
    /** 
    * Calculate levenshtein distance of the two strings. 
    * 
    * @param str1 String the first string. 
    * @param str2 String the second string. 
    * @return Integer the levenshtein distance (0 and above). 
    */ 
    get: function(str1, str2) { 
     // base cases 
     if (str1 === str2) return 0; 
     if (str1.length === 0) return str2.length; 
     if (str2.length === 0) return str1.length; 

     // two rows 
     var prevRow = new Array(str2.length + 1), 
      curCol, nextCol, i, j, tmp; 

     // initialise previous row 
     for (i=0; i<prevRow.length; ++i) { 
     prevRow[i] = i; 
     } 

     // calculate current row distance from previous row 
     for (i=0; i<str1.length; ++i) { 
     nextCol = i + 1; 

     for (j=0; j<str2.length; ++j) { 
      curCol = nextCol; 

      // substution 
      nextCol = prevRow[j] + ((str1.charAt(i) === str2.charAt(j)) ? 0 : 1); 
      // insertion 
      tmp = curCol + 1; 
      if (nextCol > tmp) { 
      nextCol = tmp; 
      } 
      // deletion 
      tmp = prevRow[j + 1] + 1; 
      if (nextCol > tmp) { 
      nextCol = tmp; 
      } 

      // copy current col value into previous (in preparation for next iteration) 
      prevRow[j] = curCol; 
     } 

     // copy last col value into previous (in preparation for next iteration) 
     prevRow[j] = nextCol; 
     } 

     return nextCol; 
    } 

    }; 

    var the_text1; 

    try { 
    the_text1 = decodeURI(r.text1).toLowerCase(); 
    } catch (ex) { 
    the_text1 = r.text1.toLowerCase(); 
    } 

    try { 
    the_text2 = decodeURI(r.text2).toLowerCase(); 
    } catch (ex) { 
    the_text2 = r.text2.toLowerCase(); 
    } 

    emit({text1: the_text1, text2: the_text2, 
     similarity: 1 - Levenshtein.get(the_text1, the_text2)/the_text1.length}); 

    }" 
) 
ORDER BY similarity DESC 
+0

Können Sie mir sagen, wo Sie die Dokumentation zu dieser JS() - Funktion finden können? Ich habe Probleme, es zu finden, obwohl ich jetzt weiß, dass es existiert. Vielen Dank! –

+0

der Link ist in der Antwort –

+0

siehe auch https://cloud.google.com/bigquery/user-defined-functions –