Написание функции Javascript в R

Я работаю с языком программирования R.

У меня есть набор данных городов и туристических маршрутов (все маршруты: Латинская Америка -> Северная Америка -> Европа -> Азия). Я создал графовую сеть этих данных:

library(igraph)

north_american_cities <- c("New York", "Los Angeles", "Chicago", "Houston", "Phoenix")
european_cities <- c("London", "Berlin", "Madrid", "Rome", "Paris")
asian_cities <- c("Tokyo", "Delhi", "Shanghai", "Beijing", "Mumbai")
latin_american_cities <- c("Lima", "Bogota", "Buenos Aires", "Sao Paulo", "Mexico City")

set.seed(123)
n <- 30
la_cities_sample <- sample(latin_american_cities, n, replace = TRUE)
na_cities_sample <- sample(north_american_cities, n, replace = TRUE)
eu_cities_sample <- sample(european_cities, n, replace = TRUE)
as_cities_sample <- sample(asian_cities, n, replace = TRUE)

df <- data.frame(LatinAmerica = la_cities_sample,
                 NorthAmerica = na_cities_sample,
                 Europe = eu_cities_sample,
                 Asia = as_cities_sample,
                 stringsAsFactors = FALSE)

df <- df[!duplicated(df), ]

edges_df <- data.frame(from = c(df$LatinAmerica, df$NorthAmerica, df$Europe),
                       to = c(df$NorthAmerica, df$Europe, df$Asia))

edge_list <- as.matrix(edges_df)

g <- graph_from_edgelist(edge_list, directed = TRUE)
plot(g)

Отсюда я написал функцию, которая берет любой город и находит все возможные маршруты путешествий, проходящие через этот город от начала до конца:

find_paths_through_city <- function(graph, target_city, path_length = 4) {
    all_paths <- all_simple_paths(graph, V(graph))
    
    valid_paths <- list()
    
    for (path in all_paths) {
        path_cities <- V(graph)[path]$name
        if (target_city %in% path_cities && length(path_cities) == path_length) {
            valid_paths <- append(valid_paths, list(path_cities))
        }
    }
    
    if (length(valid_paths) > 0) {
        paths_df <- do.call(rbind, lapply(valid_paths, function(x) as.data.frame(t(x), stringsAsFactors = FALSE)))
        colnames(paths_df) <- paste0("City", 1:path_length)
    } else {
        paths_df <- data.frame(matrix(ncol = path_length, nrow = 0))
        colnames(paths_df) <- paste0("City", 1:path_length)
    }
    
    return(paths_df)
}

Здесь я тестировал эту функцию для конкретного города:

city <- "New York"  
paths_through_city <- find_paths_through_city(g, target_city = city, path_length = 4)
unique_cities <- unique(as.vector(as.matrix(paths_through_city)))
subgraph <- induced_subgraph(g, vids = unique_cities)
plot(subgraph, vertex.size=10, vertex.label.cex=0.8, edge.arrow.size=0.5, main=paste("Subgraph of Paths Passing Through", city))

Мой вопрос: Отсюда я хочу создать интерактивный график, который позволит пользователю щелкнуть заданный узел на графике с помощью Visnetwork, а затем выделить все возможные маршруты передвижения, проходящие через этот узел.

Мои друзья и я попытались узнать, как это сделать сегодня — мы попытались написать функцию javascript для этого и проделали половину работы:

library(visNetwork)
nodes <- data.frame(id = V(g)$name, label = V(g)$name, stringsAsFactors = FALSE)
edges <- data.frame(from = edges_df$from, to = edges_df$to, stringsAsFactors = FALSE)

highlight_js <- '
function(params) {
  if (params.nodes.length == 0) return;

  var selectedNode = params.nodes[0];
  var pathLength = 4; 

  var graph = this.body.data;
  var allNodes = graph.nodes.get();
  var allEdges = graph.edges.get();

  var validPaths = [];
  function findPaths(currentPath, currentNode, depth) {
    if (depth == pathLength) {
      validPaths.push(currentPath.slice());
      return;
    }

    var connectedEdges = allEdges.filter(function(edge) {
      return edge.from == currentNode;
    });

    connectedEdges.forEach(function(edge) {
      findPaths(currentPath.concat(edge.to), edge.to, depth + 1);
    });
  }

  findPaths([selectedNode], selectedNode, 1);

  var nodesToUpdate = {};
  var edgesToUpdate = {};

  validPaths.forEach(function(path) {
    path.forEach(function(nodeId, index) {
      nodesToUpdate[nodeId] = {
        id: nodeId,
        color: "red",
        label: allNodes.find(node => node.id == nodeId).label
      };

      if (index < path.length - 1) {
        var fromNode = nodeId;
        var toNode = path[index + 1];
        var edge = allEdges.find(edge => edge.from == fromNode && edge.to == toNode);
        if (edge) {
          edgesToUpdate[edge.id] = {
            id: edge.id,
            color: "red"
          };
        }
      }
    });
  });

  graph.nodes.update(Object.values(nodesToUpdate));
  graph.edges.update(Object.values(edgesToUpdate));
}
'

visNetwork(nodes, edges) %>%
    visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE) %>%
    visPhysics(stabilization = list(iterations = 2000), solver = "barnesHut", minVelocity = 0.75) %>%
    visEvents(selectNode = highlight_js)

Как можно видеть здесь, хотя выбран азиатский город (Токио), ни один город Латинской Америки не выделен.

В исходном наборе данных это выглядит так:

> df[df$Asia == "Tokyo",]
   LatinAmerica NorthAmerica Europe  Asia
13 Buenos Aires      Houston Madrid Tokyo
15    Sao Paulo  Los Angeles  Paris Tokyo
21       Bogota     New York   Rome Tokyo
23 Buenos Aires      Houston Berlin Tokyo

Может кто-нибудь показать нам, как это исправить?

Спасибо!

Я не понимаю, как вы придумываете свои пути. Почему путь ['Bogota', 'New York', 'Paris', 'Mumbai'] не включен в ваш paths_through_city?

thothal 02.07.2024 11:41
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
234
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Предполагая, что вы действительно хотите получить все пути определенной длины, проходящие через город, вы можете сделать следующее:

Н.Б. Для более красивого оформления кода я напечатал следующие коды Javascript как JavaScript. Для использования в R вы, конечно, должны определить их как строки (экранированные JS) и передать их соответствующим функциям,

Идея

  1. При запуске виджета я сначала посещаю каждый узел и прохожу ко всем соседям, которые находятся не дальше 4 узлов. Таким образом я получаю набор всех простых путей, которые сохраняю как глобальный объект ( window.routes).
  2. При нажатии я фильтрую все пути до списка путей, содержащих выбранный узел, извлекаю все города на этих путях и все ребра и раскрашиваю их (примечание: ради многословия я создал массив идентификаторов и снова просмотрел их , вместо того, чтобы делать только один цикл, но это сделано специально, чтобы лучше понять идею).
  3. Наконец, я добавил сценарий отмены выбора, чтобы вернуть цвет к значениям по умолчанию, как только узел будет отменен.

Коды

store_all_paths

// store_all_paths <- JS("
function(el, data) {
  const network = this.network;
  function visit_node(network, city, path, path_list, max_length) {
    if (!path.includes(city)) {
      path.push(city);
      if (path.length === max_length) {
        // need to stringify because unique contraint does not work on array objects
        path_list.add(JSON.stringify(path))
      } else {
        const neighbors = network.getConnectedNodes(city, 'to');
        neighbors.forEach((nb) => visit_node(network, nb, [...path], path_list, 
                                             max_length));
      }
    }
  }
  const max_length = 4;
  const path_list = new Set();
  data.nodes.id.forEach((city) => visit_node(network, city, [], path_list, max_length));
  window.routes = [...path_list].map((rt) => JSON.parse(rt));
}
//")

handle_click

// handle_click <- JS("
function(params) {
  params.event.preventDefault();
  if (params.nodes.length > 0) {
    // 1. Find all routes including the selected nodes
    const routes = params.nodes.map(node => 
      window.routes.filter(route => route.includes(node))).flat();
    // 2. From these routes extract all involved cities and all involved edges
    const cities_on_route = [...new Set(routes.flat(2))];
    const all_edges = this.body.data.edges;
    const edges_on_route = [...new Set(routes.map(function(route) {
      const edges = [];
      for(let i = 0; i < route.length - 1; i++) {
        const edge = all_edges.get({
          filter: (item) => item.from === route[i] & item.to === route[i + 1]
        })
        edges.push(...edge)
      }
      return edges.map((e) => e.id);
    }).flat())];
    // 3. Color Edges and Nodes 
    // N.B. we could do that immediately instead of first saving all ids
    // but for illustrative purposes i made this performance wise not os smart split
    this.body.data.nodes.updateOnly(
      cities_on_route.map((id) => (
        {
          id: id, 
          color: {
            background: 'red', 
            border: 'red',
            highlight: {
              background: 'red',
              border: 'red'
            }}, 
          label: this.body.data.nodes.get(id).label
        }
      ))
    );
    this.body.data.edges.updateOnly(
      edges_on_route.map((id) => (
        {
          id: id,
          color: {
            color: 'red', 
            highlight: 'red'
          }
        }
      ))
    );
    console.info({routes: routes, edges: edges_on_route})
  }
}
//")

handle_deselect

// handle_deselect <- JS("
function(params) {
  this.body.data.nodes.updateOnly(this.body.data.nodes.get().map((node) => 
    ({
      id: node.id, 
        color: {
          background: '#D2E5FF', 
          border: '#2B7CE9',
          highlight: {
            background: '#D2E5FF',
            border: '#2B7CE9'
          }
        }
      })
    ));
  this.body.data.edges.updateOnly(this.body.data.edges.get().map((edge) => 
    ({
      id: edge.id, 
      color: {
        color: '#848484',
        highlight: '#848484',
        inherit: false
      }
    })
    )
    );  
}
//")

виснетворк

visNetwork(nodes, edges) %>%
  visOptions(nodesIdSelection = TRUE) %>%
  visPhysics(stabilization = list(iterations = 2000), solver = "barnesHut",
             minVelocity = 0.75) %>% 
  visEvents(selectNode = handle_click, deselectNode = handle_deselect) %>% 
  onRender(store_all_paths)

@thotal: огромное спасибо за чудесный ответ! Я с нетерпением жду возможности последовать вашему совету и попытаться превратить эти Java-функции в R в виде строк!

stats_noob 03.07.2024 15:02

Как вы думаете, ваш подход можно применить к этому вопросу здесь? stackoverflow.com/questions/78675817/r-multiple-dropdown-men‌​нас

stats_noob 03.07.2024 15:03

Ну, это совершенно другой вопрос (множественный выбор, и он не имеет ничего общего с этим вопросом, кроме работы с vis.

thothal 04.07.2024 14:08

Если ответ соответствует вашим потребностям, я был бы рад, если бы вы его приняли.

thothal 10.07.2024 09:11

Я думаю, что библиотека visnetwork — ваш лучший вариант — воссоздал ее с помощью следующего кода:

library(visNetwork)

# Define nodes and edges
nodes <- data.frame(id = 1:6, label = paste("Node", 1:6))
edges <- data.frame(from = c(1, 1, 2, 3, 4, 4), to = c(2, 3, 4, 5, 5, 6))

# Create the network graph
visNetwork(nodes, edges) %>%
  visNodes(
    shape = "dot",
    size = 10
  ) %>%
  visEdges(
    arrows = "to"
  ) %>%
  visInteraction(
    navigationButtons = TRUE
  )

Другие вопросы по теме