Подмножество в R дает такие же уровни, что и раньше [дубликат]

В следующем примере, который я написал, показано, как

  • обрабатывать асинхронные HTTP-вызовы;
  • Подождать ответа от каждого вызова API;
  • Использовать шаблон promise ;
  • Используйте шаблон Promise.All для объединения нескольких HTTP-вызовов;

Этот рабочий пример является автономным. Он будет определять простой объект запроса, который использует объект window XMLHttpRequest для совершения вызовов. Он будет определять простую функцию, чтобы дождаться завершения кучи обещаний.

Контекст. В этом примере запрашивается конечная точка Spotify Web API для поиска объектов playlist для заданного набора строк запроса:

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

Для каждого элемента новый Promise запустит блок - ExecutionBlock, проанализирует результат, заплатит новый набор обещаний на основе массива результатов, который представляет собой список объектов Spotify user и выполняет новый HTTP-вызов в ExecutionProfileBlock асинхронно.

Затем вы можете увидеть вложенную структуру Promise, которая позволяет вам генерировать множественные и полностью асинхронные вложенные HTTP-вызовы и присоединять результаты к каждому подмножеству вызовов через Promise.all.

NOTE Recent Spotify search API-интерфейсам потребуется указать токен доступа в заголовках запроса:

-H "Authorization: Bearer {your access token}" 

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

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "
" } } // Simple XMLHttpRequest // based on https://davidwalsh.name/xmlhttprequest SimpleRequest = { call: function(what, response) { var request; if (window.XMLHttpRequest) { // Mozilla, Safari, ... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // Internet Explorer try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } } // State changes request.onreadystatechange = function() { if (request.readyState === 4) { // Done if (request.status === 200) { // Complete response(request.responseText) } else response(); } } request.open('GET', what, true); request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken); request.send(null); } } //PromiseAll var promiseAll = function(items, block, done, fail) { var self = this; var promises = [], index = 0; items.forEach(function(item) { promises.push(function(item, i) { return new Promise(function(resolve, reject) { if (block) { block.apply(this, [item, index, resolve, reject]); } }); }(item, ++index)) }); Promise.all(promises).then(function AcceptHandler(results) { if (done) done(results); }, function ErrorHandler(error) { if (fail) fail(error); }); }; //promiseAll // LP: deferred execution block var ExecutionBlock = function(item, index, resolve, reject) { var url = "https://api.spotify.com/v1/" url += item; console.log( url ) SimpleRequest.call(url, function(result) { if (result) { var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) { return item.owner.href; }) resolve(profileUrls); } else { reject(new Error("call error")); } }) } arr = [ "search?type=playlist&q=%22doom%20metal%22", "search?type=playlist&q=Adele" ] promiseAll(arr, function(item, index, resolve, reject) { console.log("Making request [" + index + "]") ExecutionBlock(item, index, resolve, reject); }, function(results) { // Aggregated results console.log("All profiles received " + results.length); //console.log(JSON.stringify(results[0], null, 2)); ///// promiseall again var ExecutionProfileBlock = function(item, index, resolve, reject) { SimpleRequest.call(item, function(result) { if (result) { var obj = JSON.parse(result); resolve({ name: obj.display_name, followers: obj.followers.total, url: obj.href }); } //result }) } //ExecutionProfileBlock promiseAll(results[0], function(item, index, resolve, reject) { //console.log("Making request [" + index + "] " + item) ExecutionProfileBlock(item, index, resolve, reject); }, function(results) { // aggregated results console.log("All response received " + results.length); console.log(JSON.stringify(results, null, 2)); } , function(error) { // Error console.log(error); }) ///// }, function(error) { // Error console.log(error); });

Я подробно рассмотрел это решение здесь .

437
задан zx8754 27 May 2015 в 11:17
поделиться

12 ответов

Все, что вам нужно сделать, это снова применить множитель () к вашей переменной после подмножества:

> subdf$letters
[1] a b c
Levels: a b c d e
subdf$letters <- factor(subdf$letters)
> subdf$letters
[1] a b c
Levels: a b c

ИЗМЕНИТЬ

Пример примера с параметрами:

factor(ff)      # drops the levels that do not occur

Для удаления уровней из всех столбцов факторов в кадре данных вы можете использовать:

subdf <- subset(df, numbers <= 3)
subdf[] <- lapply(subdf, function(x) if(is.factor(x)) factor(x) else x)
340
ответ дан hatmatrix 5 September 2018 в 07:01
поделиться

Вот еще один способ, который, я считаю, эквивалентен подходу factor(..):

> df <- data.frame(let=letters[1:5], num=1:5)
> subdf <- df[df$num <= 3, ]

> subdf$let <- subdf$let[ , drop=TRUE]

> levels(subdf$let)
[1] "a" "b" "c"
10
ответ дан ars 5 September 2018 в 07:01
поделиться

Для полноты, теперь есть fct_drop в пакете forcats http://forcats.tidyverse.org/reference/fct_drop.html .

Он отличается от droplevels тем, как он имеет дело с NA:

f <- factor(c("a", "b", NA), exclude = NULL)

droplevels(f)
# [1] a    b    <NA>
# Levels: a b <NA>

forcats::fct_drop(f)
# [1] a    b    <NA>
# Levels: a b
6
ответ дан Aurèle 5 September 2018 в 07:01
поделиться

Я написал служебные функции для этого. Теперь, когда я знаю о drop.levels gdata, он выглядит довольно похожим. Здесь они (из здесь ):

present_levels <- function(x) intersect(levels(x), x)

trim_levels <- function(...) UseMethod("trim_levels")

trim_levels.factor <- function(x)  factor(x, levels=present_levels(x))

trim_levels.data.frame <- function(x) {
  for (n in names(x))
    if (is.factor(x[,n]))
      x[,n] = trim_levels(x[,n])
  x
}
5
ответ дан Brendan OConnor 5 September 2018 в 07:01
поделиться

Другой способ сделать то же самое, но с dplyr

library(dplyr)
subdf <- df %>% filter(numbers <= 3) %>% droplevels()
str(subdf)

Изменить:

Также работает! Благодаря agenis

subdf <- df %>% filter(numbers <= 3) %>% droplevels
levels(subdf$letters)
14
ответ дан Community 5 September 2018 в 07:01
поделиться

здесь есть способ сделать это

varFactor <- factor(letters[1:15])
varFactor <- varFactor[1:5]
varFactor <- varFactor[drop=T]
6
ответ дан David Arenburg 5 September 2018 в 07:01
поделиться

Очень интересная нить, мне особенно понравилась идея просто подставить подзаголовок снова. Раньше у меня была аналогичная проблема, и я просто перешел к символу, а затем вернулся к коэффициенту.

   df <- data.frame(letters=letters[1:5],numbers=seq(1:5))
   levels(df$letters)
   ## [1] "a" "b" "c" "d" "e"
   subdf <- df[df$numbers <= 3]
   subdf$letters<-factor(as.character(subdf$letters))
4
ответ дан DfAC 5 September 2018 в 07:01
поделиться

Это известная проблема, и одно возможное решение предоставляется drop.levels() в пакете gdata , где ваш пример становится

> drop.levels(subdf)
  letters numbers
1       a       1
2       b       2
3       c       3
> levels(drop.levels(subdf)$letters)
[1] "a" "b" "c"

Существует также dropUnusedLevels в пакете Hmisc . Однако он работает только путем изменения оператора подмножества [ и здесь неприменим.

В качестве следствия прямой подход на основе столбца является простым as.factor(as.character(data)):

> levels(subdf$letters)
[1] "a" "b" "c" "d" "e"
> subdf$letters <- as.factor(as.character(subdf$letters))
> levels(subdf$letters)
[1] "a" "b" "c"
33
ответ дан Dirk Eddelbuettel 5 September 2018 в 07:01
поделиться

Если вы не хотите этого поведения, не используйте факторы, используйте вместо него векторы символов. Я думаю, что это имеет больше смысла, чем исправление. Попробуйте выполнить следующие действия перед загрузкой данных с помощью read.table или read.csv:

options(stringsAsFactors = FALSE)

Недостатком является то, что вы ограничены алфавитным порядком. (переупорядочить ваш друг для сюжетов)

36
ответ дан hadley 5 September 2018 в 07:01
поделиться

Глядя на код droplevels методов в источнике R, вы можете видеть , он обтекает функцию factor. Это означает, что вы можете в основном воссоздать столбец с функцией factor. Ниже data.table способ сбросить уровни из всех столбцов факторов.

library(data.table)
dt = data.table(letters=factor(letters[1:5]), numbers=seq(1:5))
levels(dt$letters)
#[1] "a" "b" "c" "d" "e"
subdt = dt[numbers <= 3]
levels(subdt$letters)
#[1] "a" "b" "c" "d" "e"

upd.cols = sapply(subdt, is.factor)
subdt[, names(subdt)[upd.cols] := lapply(.SD, factor), .SDcols = upd.cols]
levels(subdt$letters)
#[1] "a" "b" "c"
6
ответ дан jangorecki 5 September 2018 в 07:01
поделиться

Это неприятно. Так я обычно делаю это, чтобы не загружать другие пакеты:

levels(subdf$letters)<-c("a","b","c",NA,NA)

, который получает вас:

> subdf$letters
[1] a b c
Levels: a b c

Обратите внимание, что новые уровни заменят все, что занимает их индекс в старые значения (subdf $ letters), поэтому что-то вроде:

levels(subdf$letters)<-c(NA,"a","c",NA,"b")

не будет работать.

Это, очевидно, не идеально, когда у вас много уровней, но для Несколько, это быстро и легко.

6
ответ дан Matt Parker 5 September 2018 в 07:01
поделиться

Поскольку R версии 2.12, есть функция droplevels().

levels(droplevels(subdf$letters))
456
ответ дан Roman Luštrik 5 September 2018 в 07:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: