英文:
How to use fuzzysearch with Shiny in R?
问题
我正在尝试在DT数据表中使用这个JS脚本(来自此网站:https://datatables.net/blog/2021-09-17):
var fsrco = $('#fuzzy-ranking').DataTable({
fuzzySearch: {
rankColumn: 3
},
sort: [[3, 'desc']]
});
fsrco.on('draw', function(){
fsrco.order([3, 'desc']);
});
使用此脚本标签:
"//cdn.datatables.net/plug-ins/1.11.3/features/fuzzySearch/dataTables.fuzzySearch.js"
我想将它合并到一个Shiny应用程序中的DT数据表函数中,其中模糊搜索是根据排名顺序应用的(相似性较高的在顶部),但是我不想显示排名列。
英文:
I am trying to use this JS script (from this website: https://datatables.net/blog/2021-09-17) in a DT datatable:
var fsrco = $('#fuzzy-ranking').DataTable({
fuzzySearch: {
rankColumn: 3
},
sort: [[3, 'desc']]
});
fsrco.on('draw', function(){
fsrco.order([3, 'desc']);
});
Using this script tag:
"//cdn.datatables.net/plug-ins/1.11.3/features/fuzzySearch/dataTables.fuzzySearch.js"
I want to incorporate it in a DT datable function within a Shiny app where the fuzzy search is applied using the rank order (with higher similarity on top), however, I do not want the rank column to be shown.
Something like this, but without showing the Rank column.
Some base regular example:
library(shiny)
library(DT)
js <- c(
" var fsrco = $('#fuzzy-ranking').DataTable({",
" fuzzySearch: {",
" rankColumn: 3",
" },",
" sort: [[3, 'desc']]",
"});",
"fsrco.on('draw', function(){",
" fsrco.order([3, 'desc']);",
"});"
)
ui <- fluidPage(
DTOutput("table")
)
server <- function(input, output, session){
output[["table"]] <- renderDT({
datatable(
iris,
selection = "none",
editable = TRUE,
callback = JS(js),
extensions = "KeyTable",
options = list(
keys = TRUE,
url = "//cdn.datatables.net/plug-ins/1.11.3/features/fuzzySearch/dataTables.fuzzySearch.js"
)
)
})
}
shinyApp(ui, server)
答案1
得分: 2
这个插件是一个旧版本的插件,在最新版本的DataTables上不起作用。
但是我们可以使用JavaScript函数来计算相似度,并在SearchBuilder扩展中自定义搜索。
首先,复制以下JavaScript代码并保存为levenshtein.js:
/*
BSD 2-Clause License
Copyright (c) 2018, Tadeusz Łazurski
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
function levenshtein(__this, that, limit) {
var thisLength = __this.length,
thatLength = that.length,
matrix = [];
// 如果未定义限制,则将从这和那的参数中计算出来。
limit = (limit || (thatLength > thisLength ? thatLength : thisLength)) + 1;
for (var i = 0; i < limit; i++) {
matrix[i] = [i];
matrix[i].length = limit;
}
for (i = 0; i < limit; i++) {
matrix[0][i] = i;
}
if (Math.abs(thisLength - thatLength) > (limit || 100)) {
return prepare(limit || 100);
}
if (thisLength === 0) {
return prepare(thatLength);
}
if (thatLength === 0) {
return prepare(thisLength);
}
// 计算矩阵。
var j, this_i, that_j, cost, min, t;
for (i = 1; i <= thisLength; ++i) {
this_i = __this[i - 1];
// 步骤 4
for (j = 1; j <= thatLength; ++j) {
// 检查到目前为止的倾斜 ld 总数
if (i === j && matrix[i][j] > 4) return prepare(thisLength);
that_j = that[j - 1];
cost = this_i === that_j ? 0 : 1; // 步骤 5
// 计算最小值(比Math.min(...)要快得多)。
min = matrix[i - 1][j] + 1; // 删除。
if ((t = matrix[i][j - 1] + 1) < min) min = t; // 插入。
if ((t = matrix[i - 1][j - 1] + cost) < min) min = t; // 替换。
// 更新矩阵。
matrix[i][j] =
i > 1 &&
j > 1 &&
this_i === that[j - 2] &&
__this[i - 2] === that_j &&
(t = matrix[i - 2][j - 2] + cost) < min
? t
: min; // 转置。
}
}
return prepare(matrix[thisLength][thatLength]);
function prepare(steps) {
var length = Math.max(thisLength, thatLength);
var relative = length === 0 ? 0 : steps / length;
var similarity = 1 - relative;
return {
steps: steps,
relative: relative,
similarity: similarity
};
}
}
现在,这是R代码:
library(DT)
dtable <- datatable(
iris,
extensions = "SearchBuilder",
options = list(
dom = "Qlfrtip",
searchBuilder = list(
conditions = list(
string = list(
fuzzy = list(
conditionName = "Fuzzy search",
init = JS(
"function (that, fn, preDefined = null) {",
" var el = $('<input/>').on('input', function() { fn(that, this) });",
" if (preDefined !== null) {",
" $(el).val(preDefined[0]);",
" }",
" return el;",
"}"
),
inputValue = JS(
"function (el) {",
" return [$(el[0]).val()];",
"}"
),
isInputValid = JS(
"function (el, that) {",
" return $(el[0]).val().length !== 0;",
"}"
),
search = JS(
"function (value, pattern) {",
" var fuzzy = levenshtein(value, pattern[0]);",
" return fuzzy.similarity > 0.25;",
"}"
)
)
)
)
)
)
)
path <- normalizePath("path/to") # 包含levenshtein.js的文件夹
dep <- htmltools::htmlDependency(
"Levenshtein", "1.0.0",
path, script = "levenshtein.js")
dtable$dependencies <- c(dtable$dependencies, list(dep))
dtable
必须选择一个相似度的阈值。在这里,我选择了0.25
:
return fuzzy.similarity > 0.25;
编辑
在Shiny中使用时,请使用server=FALSE
:
renderDT({
dtable
}, server = FALSE)
英文:
This plugin is an old one and it doesn't work on recent versions of DataTables.
But we can take the JavaScript function that computes the similarity and use it in a custom search with the SearchBuilder extension.
First, copy this JavaScript code and save it under the name levenshtein.js:
/*
BSD 2-Clause License
Copyright (c) 2018, Tadeusz Łazurski
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
function levenshtein(__this, that, limit) {
var thisLength = __this.length,
thatLength = that.length,
matrix = [];
// If the limit is not defined it will be calculate from this and that args.
limit = (limit || (thatLength > thisLength ? thatLength : thisLength)) + 1;
for (var i = 0; i < limit; i++) {
matrix[i] = [i];
matrix[i].length = limit;
}
for (i = 0; i < limit; i++) {
matrix[0][i] = i;
}
if (Math.abs(thisLength - thatLength) > (limit || 100)) {
return prepare(limit || 100);
}
if (thisLength === 0) {
return prepare(thatLength);
}
if (thatLength === 0) {
return prepare(thisLength);
}
// Calculate matrix.
var j, this_i, that_j, cost, min, t;
for (i = 1; i <= thisLength; ++i) {
this_i = __this[i - 1];
// Step 4
for (j = 1; j <= thatLength; ++j) {
// Check the jagged ld total so far
if (i === j && matrix[i][j] > 4) return prepare(thisLength);
that_j = that[j - 1];
cost = this_i === that_j ? 0 : 1; // Step 5
// Calculate the minimum (much faster than Math.min(...)).
min = matrix[i - 1][j] + 1; // Devarion.
if ((t = matrix[i][j - 1] + 1) < min) min = t; // Insertion.
if ((t = matrix[i - 1][j - 1] + cost) < min) min = t; // Substitution.
// Update matrix.
matrix[i][j] =
i > 1 &&
j > 1 &&
this_i === that[j - 2] &&
__this[i - 2] === that_j &&
(t = matrix[i - 2][j - 2] + cost) < min
? t
: min; // Transposition.
}
}
return prepare(matrix[thisLength][thatLength]);
function prepare(steps) {
var length = Math.max(thisLength, thatLength);
var relative = length === 0 ? 0 : steps / length;
var similarity = 1 - relative;
return {
steps: steps,
relative: relative,
similarity: similarity
};
}
}
Now, here is the R code:
library(DT)
dtable <- datatable(
iris,
extensions = "SearchBuilder",
options = list(
dom = "Qlfrtip",
searchBuilder = list(
conditions = list(
string = list(
fuzzy = list(
conditionName = "Fuzzy search",
init = JS(
"function (that, fn, preDefined = null) {",
" var el = $('<input/>').on('input', function() { fn(that, this) });",
" if (preDefined !== null) {",
" $(el).val(preDefined[0]);",
" }",
" return el;",
"}"
),
inputValue = JS(
"function (el) {",
" return [$(el[0]).val()];",
"}"
),
isInputValid = JS(
"function (el, that) {",
" return $(el[0]).val().length !== 0;",
"}"
),
search = JS(
"function (value, pattern) {",
" var fuzzy = levenshtein(value, pattern[0]);",
" return fuzzy.similarity > 0.25;",
"}"
)
)
)
)
)
)
)
path <- normalizePath("path/to") # the folder containing levenshtein.js
dep <- htmltools::htmlDependency(
"Levenshtein", "1.0.0",
path, script = "levenshtein.js")
dtable$dependencies <- c(dtable$dependencies, list(dep))
dtable
One has to choose a threshold for the similarity. Here I took 0.25
:
return fuzzy.similarity > 0.25;
Edit
For usage in Shiny, use server=FALSE
:
renderDT({
dtable
}, server = FALSE)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论