6

I have this dataframe:

Lat        Long
59 44 50   151 45 11
59 49 28   154 52 56
59 46 42   150 45 15

How can I convert this into decimal columns?

lat is in dd mm ss and long is in ddd mm ss

I found a similar solution here, but couldn't adapt the regex for my case.

Converting geo coordinates from degree to decimal

Community
  • 1
  • 1
maximusdooku
  • 5,242
  • 10
  • 54
  • 94

3 Answers3

13

Try this function:

angle2dec <- function(angle) {
  angle <- as.character(angle)
  x <- do.call(rbind, strsplit(angle, split=' '))
  x <- apply(x, 1L, function(y) {
    y <- as.numeric(y)
    y[1] + y[2]/60 + y[3]/3600
  })
  return(x)
}

Then you can apply it to each column in your data frame:

new_df <- apply(df, 2L, angle2dec)
new_df
          Lat     Long
[1,] 59.74722 151.7531
[2,] 59.82444 154.8822
[3,] 59.77833 150.7542

or just

df$Lat <- angle2dec(df$Lat)
df$Long <- angle2dec(df$Long)
Bridgeburners
  • 637
  • 5
  • 15
  • I am getting this: Error in strsplit(angle, split = " ") : non-character argument – maximusdooku Jun 16 '15 at 22:55
  • Oh, I assumed that the class of your columns was "character". What is it, factor? Well anyhow, try the edit I made. – Bridgeburners Jun 16 '15 at 22:59
  • Thanks! It works now. But I am getting this warning message : Warning message: In (function (..., deparse.level = 1) : number of columns of result is not a multiple of vector length (arg 22) What does it mean? – maximusdooku Jun 16 '15 at 23:00
  • What is the original class of your columns? – Bridgeburners Jun 16 '15 at 23:01
  • It's factor for Lat/Long. There are other columns as well. but I am not operating on them right now – maximusdooku Jun 16 '15 at 23:02
  • Also, I think there is a sign error. Shouldn't the Longitudes be -ve? – maximusdooku Jun 16 '15 at 23:05
  • I can't seem to reproduce your error. How are you executing the function? – Bridgeburners Jun 16 '15 at 23:08
  • It's just a warning, not an error. Maybe because I have a much larger databse that I can't give here. However, the long should be negative I think. I tried plotting my database in the US map and it's off. – maximusdooku Jun 16 '15 at 23:09
  • by convention, people usually use negative for West longs and South lats. However, that wasn't in your example :) you could add logic to detect N/S and E/W – C8H10N4O2 Jun 16 '15 at 23:12
  • 1
    Well there are different conventions for longitude. Some use values between 0 and 360, others between -180 and 180. The convention of your data frame wasn't specified, but no matter which convention it is, what I gave you should be the correct conversion from minutes & seconds to decimal. You can try the appropriate transformation to the result. (For example, try Long - 180 in case the map you're using uses the -180 to 180 convention.) – Bridgeburners Jun 16 '15 at 23:28
4

May I suggest the tidyr approach:

df <- data.frame( Lat=c("59 44 50","59 49 28","59 46 42"),
                 Long=c("151 45 11","154 52 56","150 45 15"))

library(tidyr); library(dplyr)
df %>% 
  separate(Lat, paste("lat",c("d","m","s"), sep="_") ) %>%
  separate(Long, paste("long",c("d","m","s"), sep="_" ) ) %>%
  mutate_each(funs(as.numeric)) %>%
  transmute(lat_dec=lat_d + lat_m/60 + lat_s/60^2,
            long_dec=long_d + long_m/60 + long_s/60^2)

#    lat_dec long_dec
# 1 59.74722 151.7531
# 2 59.82444 154.8822
# 3 59.77833 150.7542
C8H10N4O2
  • 18,312
  • 8
  • 98
  • 134
  • Is this the preferred method of doing it in R than the strsplit way of the other answer? just curious. – maximusdooku Jun 16 '15 at 23:03
  • Writing the function is probably the better way since you write once and use twice. I was going to do the strsplit, but the other fellow beat me to it. – C8H10N4O2 Jun 16 '15 at 23:05
  • 1
    You are most welcome. I think Bridgeburners demonstrated some important tools in base (do.call, rbind, apply), and once you learn those then dplyr and tidyr packages are definitely worth checking out. – C8H10N4O2 Jun 16 '15 at 23:10
3

Here's an idea using splitstackshape:

library(dplyr)
library(splitstackshape)

df %>% 
  cSplit(c("Lat", "Long"), sep = " ") %>%
  transmute(Lat = Lat_1 + Lat_2 / 60 + Lat_3 / 60^2,
            Long = Long_1 + Long_2 / 60 + Long_3 / 60^2)

Which gives:

#        Lat     Long
#1: 59.74722 151.7531
#2: 59.82444 154.8822
#3: 59.77833 150.7542
Steven Beaupré
  • 21,343
  • 7
  • 57
  • 77