4
votes

Utiliser case_when () pour attribuer deux nouvelles colonnes au lieu d'une

J'ai cet exemple de données:

New York: 40.7128, 74.0060
Boston: 42.3601, 71.0589
Chicago: 41.8781, 87.6298
Cleveland: 41.4993, 81.6944
Atlanta: 33.7490, 84.3880

Supposons que City1 est le point de départ et que City2 est la destination. C'est-à-dire qu'une personne a voyagé de New York à Chicago.

Je veux ajouter une colonne pour la latitude de départ et une colonne pour la longitude de départ, et faire de même pour la ville de destination. En tout, je veux quatre nouvelles colonnes. J'ai déjà les coordonnées.

Comment puis-je attribuer les coordonnées? J'ai essayé d'utiliser case_when , mais je ne sais pas comment fournir des coordonnées à plusieurs colonnes. Il est facile de faire une colonne:

library(tidyverse)

# The numbers after the cities are the latitudes
df <- df %>% 
  mutate(
   City1_lat = case_when(
    City1 == 'New York' ~ 40.7128,
    City1 == 'Boston' ~ 42.3601,
    City1 == 'Chicago' ~ 41.8781
  )
 )

Comment puis-je développer cela pour ajouter une colonne City1_lon ? Essayer de rationaliser cela autant que possible, car j'ai plusieurs milliers de lignes d'origines / destinations. Une solution dplyr ou base fonctionne. J'étendrais ceci pour les villes de destination, City2 . Pour référence:

df <- tibble(
  "City1" = c("New York", "Boston", "Chicago"),
  "City2" = c("Chicago", "Cleveland", "Atlanta"))


0 commentaires

5 Réponses :


2
votes

Avec les données de votre ville dans un cadre de données comme celui-ci:

> df = cbind(df, setNames(city[match(df$City1, city$City), c("lat","long")],c("City1lat","City1long")), setNames(city[match(df$City2, city$City), c("lat","long")],c("City2lat","City2long")))
> df
     City1     City2 City1lat City1long City2lat City2long
1 New York   Chicago  40.7128   74.0060  41.8781   87.6298
2   Boston Cleveland  42.3601   71.0589  41.4993   81.6944
3  Chicago   Atlanta  41.8781   87.6298  33.7490   84.3880

Utilisez match pour rechercher les noms de villes dans les tables, extraire la lat-long et renommer donne ceci:

> setNames(city[match(df$City1, city$City), c("lat","long")],c("City1lat","City1long"))
  City1lat City1long
1  40.7128   74.0060
2  42.3601   71.0589
3  41.8781   87.6298

> setNames(city[match(df$City2, city$City), c("lat","long")],c("City2lat","City2long"))
  City2lat City2long
3  41.8781   87.6298
4  41.4993   81.6944
5  33.7490   84.3880

que vous pouvez cbind sur vos données d'origine:

> city
       City     lat    long
1  New York 40.7128 74.0060
2    Boston 42.3601 71.0589
3   Chicago 41.8781 87.6298
4 Cleveland 41.4993 81.6944
5   Atlanta 33.7490 84.3880


0 commentaires

2
votes

Une option est de faire un left_join après avoir créé un ensemble de données 'keyval'

keyval <- structure(list(City = c("New York", "Boston", "Chicago", "Cleveland", 
 "Atlanta"), lat = c(40.7128, 42.3601, 41.8781, 41.4993, 33.749
 ), lon = c(74.0068, 71.0589, 87.6298, 81.6944, 84.388)), row.names = c(NA, 
  -5L), class = c("tbl_df", "tbl", "data.frame"))

S'il y a plus de colonnes dans les données d'origine et que nous sommes intéressé uniquement par les colonnes "Ville", puis parcourez uniquement les colonnes "Ville"

df$journeys <- (100,200,300)
nm1 <- grep("City", names(df), value = TRUE)
map_dfc(nm1, ~  df %>% 
                     select(.x) %>% 
                     left_join(keyval, by = setNames('City', .x))) %>%  
      bind_cols(df %>% 
                  select(-one_of(nm1)))

données

library(tidyverse)
map_dfc(names(df), ~  df %>% 
                        select(.x) %>% 
                        left_join(keyval, by = setNames('City', .x))) %>%
    select(names(df), everything())  
# A tibble: 3 x 6
#  City1    City2       lat   lon  lat1  lon1
#  <chr>    <chr>     <dbl> <dbl> <dbl> <dbl>
#1 New York Chicago    40.7  74.0  41.9  87.6
#2 Boston   Cleveland  42.4  71.1  41.5  81.7
#3 Chicago  Atlanta    41.9  87.6  33.7  84.4


5 commentaires

Le jeu de valeurs de clé doit-il être la longueur du jeu de données réel? Je travaille avec plusieurs milliers de lignes. Donc, évidemment, un ensemble plus petit serait génial


@papelr Pour faciliter la création, les noms d'états peuvent être obtenus à partir de state.name


Cela semble échouer s'il y a d'autres colonnes en dehors de City1 et City2 dans les données source, par exemple si df $ joursneys = c (100,200,300) .


Oui, mais la robustesse est toujours meilleure. Je n'ai pas compris comment faire fonctionner votre code uniquement sur deux colonnes sans d'abord supprimer toutes les autres colonnes.


@Spacedman Je suppose que la partie défaillante est la dernière étape select (names (df) , mais je supprime cela, puis j'ajoute les autres colonnes, cela devrait fonctionner correctement



1
votes

Voici une solution tidyverse:

library(dplyr)
library(purrr)

df <- tibble(
  "City1" = c("New York", "Boston", "Chicago"),
  "City2" = c("Chicago", "Cleveland", "Atlanta"))


df <- df %>% 
  mutate(
    City1_coords = case_when(
      City1 == 'New York' ~ list(c(40.7128,74.0060)),
      City1 == 'Boston' ~ list(c(42.3601,71.0589)),
      City1 == 'Chicago' ~ list(c(41.8781,87.6298))
    )
  ) %>% 
  mutate(City1_lat = City1_coords %>% map_dbl(~ .x[1] ),
         City1_lon = City1_coords %>% map_dbl(~ .x[2] ))


3 commentaires

Est-ce que cela a fait et a obtenu l'erreur suivante: Erreur dans mutate_impl (.data, points): Erreur d'évaluation: Le résultat 125 n'est pas un vecteur atomique de longueur 1. Mes variables sont-elles du mauvais type? Tout ce que je peux penser. L'exemple ci-dessus fonctionne parfaitement, mais uniquement sur cet exemple. Hmm


C'était le problème avec cette réponse: github.com/tidyverse/purrr/issues/337


Content que vous l'ayez compris!



0
votes

Vous devez appeler en externe un fichier (dans mon exemple, appelé data_xy) avec des informations avec "city, lat and long", puis vous pouvez utiliser left_join. Essayez ce code:

> df_latlon
[[1]]
# A tibble: 3 x 3
  City1      lat   lon
  <chr>    <dbl> <dbl>
1 New York  40.7  74.0
2 Boston    42.4  71.1
3 Chicago   41.9  87.6

[[2]]
# A tibble: 3 x 3
  City2       lat   lon
  <chr>     <dbl> <dbl>
1 Chicago    41.9  87.6
2 Cleveland  41.5  81.7
3 Atlanta    33.7  84.4

Le résultat:

library(dplyr)
library(purrr)
data_xy <- tibble(city = c("New York", "Boston", "Chicago", "Cleveland", "Atlanta"),
                  lat = c(40.7128, 42.3601, 41.8781, 41.4993, 33.7490),
                  lon = c(74.0060, 71.0589, 87.6298, 81.6944, 84.3880))


df <- tibble("City1" = c("New York", "Boston", "Chicago"),
             "City2" = c("Chicago", "Cleveland", "Atlanta"))

df_latlon <- map(names(df), ~ left_join(df %>% select(.x),  data_xy, 
                                        by= structure(names = .x, .Data = "city")) )
df_latlon


0 commentaires

1
votes

Voici un moyen de le faire en utilisant mutate_all et unnest , avec un bonus pour nommer les colonnes:

df %>% 
  mutate_all(funs(l = case_when(
      . == 'New York'  ~ list(tibble(at=40.7128, on=74.0060)),
      . == 'Boston'    ~ list(tibble(at=42.3601, on=71.0589)),
      . == 'Chicago'   ~ list(tibble(at=41.8781, on=87.6298)),
      . == 'Cleveland' ~ list(tibble(at=41.4993, on=81.6944)),
      . == 'Atlanta'   ~ list(tibble(at=33.7490, on=84.3880))
    )
  )) %>%
  unnest(.sep = "")

# # A tibble: 3 x 6
#      City1     City2 City1_lat City1_lon City2_lat City2_lon
#      <chr>     <chr>     <dbl>     <dbl>     <dbl>     <dbl>
# 1 New York   Chicago   40.7128   74.0060   41.8781   87.6298
# 2   Boston Cleveland   42.3601   71.0589   41.4993   81.6944
# 3  Chicago   Atlanta   41.8781   87.6298   33.7490   84.3880

Ceci adresses "Utilisation de case_when () pour attribuer deux nouvelles colonnes" .

Pour résoudre le problème général, je recommanderais une solution basée sur les jointures à gauche, car il est plus flexible d'avoir votre clés et valeurs dans un tableau séparé soigné.


4 commentaires

Je pense que je suis aveugle. Où est le hack - mais j'aime beaucoup cette solution


mutate_all créera des colonnes nommées City1_l et City2_l , puis unnest , grâce à l'argument sep = '' créera de nouvelles colonnes en les concaténant avec les colonnes tibble nommées 'at et on . C'est le hack :)


Une manière plus conventionnelle aurait été d'écrire funs (gps = ... et tibble (lat = 40.7128, lon = 74.0060)) etc, puis en utilisant sep = '_' vous auriez donné des colonnes nommées City1_gps_lat etc


Ahhhhhh je le vois