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"))
5 Réponses :
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
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)))
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
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
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] ))
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!
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
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é.
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