R наборы координат извлекаются из строки

Я пытаюсь извлечь наборы координат из строк и изменить формат.

Я пробовал некоторые из пакетов stringr и ничего не добился с извлечением паттернов. Я впервые имею дело с регулярным выражением и все еще немного запутываю создание шаблона.

Есть фрейм данных с одним столбцом с одним или несколькими наборами координат. Единственный шаблон (большинство), отделяющий Lat от Long - (-), а для разделения одного набора координат на другой используется (/)

Вот пример некоторых данных:

ID  Coordinates
1   3438-5150
2   3346-5108/3352-5120 East island, South port
3   West coast (284312 472254)
4   28.39.97-47.05.62/29.09.13-47.44.03
5   2843-4722/3359-5122(1H-2H-3H-4F)

Большая часть данных представлена ​​в десятичной степени, например (id 1 - это широта 34,38, долгота 51,50), некоторые другие находятся в 00º00'00 '', например (id 4 - 28º 39 '97' широты 47º 05 '62' 'долготы)

Мне нужно будет сделать в несколько шагов

1 - Извлечь все наборы координат, создав новую строку для каждого набора каждой записи;

2 - Извлечь текстовую метку записи в новый столбец, объединив их;

3- Преобразуйте координаты из 00º00'00 '' (28.39.97) в 00.0000º (28.6769 - десятичное число), чтобы все координаты были в одном формате. Я легко могу преобразовать, если они числовые.

4 - Добавьте точку (.), Чтобы отделить десятичные значения градуса (от 3438 до 34,38), и добавьте (-), чтобы идентифицировать как (-34,38) юго-западное полушарие. Все значения должны иметь знак (-).

Я пытаюсь получить что-то вроде этого:

Шаг 1 и 2 - Извлечение наборов координат и имен

ID  x           y          label
1   3438        5150      
2   3346        5108      East island, South port
2   3352        5120      East island, South port
3   284312      472254    West coast
4   28.39.97    47.05.62    
4   29.09.13    47.44.03
5   2843        4722      1H-2H-3H-4F
5   3359        5122      1H-2H-3H-4F

Шаг 3 - преобразовать формат координат в десятичный градус (ID 4)

ID  x           y       label
1   3438        5150    
2   3346        5108    East island, South port
2   3352        5120    East island, South port
3   284312      472254  West coast
4   286769      471005  
4   291536      470675
5   2843        4722      1H-2H-3H-4F
5   3359        5122      1H-2H-3H-4F

Шаг 4 - изменить формат отображения

ID   x          y         label
1   -34.38      -51.50    
2   -33.46      -51.08    East island, South port
2   -33.52      -51.20    East island, South port
3   -28.43      -47.22    West coast
4   -28.6769    -47.1005    
4   -29.1536    -47.0675
5   -28.43      -47.22    1H-2H-3H-4F
5   -33.59      -51.22    1H-2H-3H-4F   

Я отредактировал вопрос, чтобы лучше прояснить свои проблемы и изменить некоторые из моих потребностей. Я понял, что это нелегко понимать.

Итак, кто-нибудь работал с чем-то подобным? Любое другое предложение было бы большим подспорьем.

Еще раз спасибо за то, что нашли время, чтобы помочь.


person aoceano    schedule 18.08.2016    source источник
comment
Регулярное выражение для извлечения всех ваших координат: (\d{2})\.?(\d{2})(?:\.(\d{2}))?-(\d{2})\.?(\d{2})(?:\.(\d{2}))?. Вам нужно искать его глобально (модификатор g). Затем, если $3 и $6 не пустые, у вас есть минуты и секунды, поэтому преобразуйте их в десятичные дроби степени (в вашем R-коде). К сожалению, я не очень помог, когда дело доходит до R, поэтому, возможно, кто-то другой подберет регулярное выражение и подготовит пример R.   -  person Dmitry Egorov    schedule 18.08.2016
comment
Вы можете уточнить свой второй вопрос? Этот вариант использования не ясен. Можете ли вы добавить входные данные для вашего второго примера и соответствующий результат?   -  person steveb    schedule 18.08.2016
comment
Большое спасибо @DmitryEgorov, это поможет лучше понять шаблон регулярного выражения.   -  person aoceano    schedule 18.08.2016
comment
@steveb, мой второй пример - это еще один способ извлечения координат. Вместо того, чтобы создавать новые столбцы для каждого нового набора одной и той же записи, я бы создал новые строки с тем же идентификатором. Несколько записей содержат до 5 наборов координат в одной строке. Это скорее вопрос о том, будет ли это хорошим решением.   -  person aoceano    schedule 18.08.2016
comment
@steveb Я изменил вопрос, чтобы лучше прояснить мою ситуацию, и улучшил пример.   -  person aoceano    schedule 18.08.2016
comment
@aoceano В сообщении больше нет исходных данных, а также это означает, что некоторые образцы формата данных больше не существуют. Я имею в виду недостающие данные 2843-4722/3359-5122(1H-2H-3H-4F). Рекомендуется включить все варианты использования, которые вам нужно решить, и ожидаемый результат для всех вариантов использования. Это облегчит решение всех ситуаций.   -  person steveb    schedule 18.08.2016
comment
@steveb Спасибо. Я забыл отрегулировать шаг 4. 2843-4722 / 3359-5122 (1H-2H-3H-4F) был удален, так как он находится под номером 3346-5108 / 3352-5120 Восточный остров, Южный порт. (1H-2H-3H-4F) точно как имя. Я добавил западное побережье (284312 472254), поскольку координаты находятся внутри () и не имеют (-) для их разделения.   -  person aoceano    schedule 18.08.2016
comment
@aoceano Как определить, должны ли числовые значения быть отрицательными? Чтобы уточнить, будут ли данные формы 2843-4722/3359-5122(1H-2H-3H-4F) по-прежнему присутствовать в вашем наборе данных?   -  person steveb    schedule 18.08.2016
comment
@steveb все значения отрицательные. Все данные взяты из Юго-Западного полушария. Поэтому люди склонны не использовать (-). Форма данных 2843-4722 / 3359-5122 (1H-2H-3H-4F) все еще находится в данных. Поскольку я считаю (1H-2H-3H-4F) именем, я обменялся с другими данными, чтобы попытаться сократить пример.   -  person aoceano    schedule 18.08.2016
comment
Для полноты, пожалуйста, добавьте этот вариант использования к первоначальному вопросу. Если есть другие, которых нет, вы также должны добавить их.   -  person steveb    schedule 18.08.2016
comment
@steveb, все готово. все случаи, найденные в наборе данных. Не могу отблагодарить вас за то, что нашли время помочь мне.   -  person aoceano    schedule 18.08.2016
comment
@aoceano К вашему сведению, я только что заметил, что у вас теперь два выходных столбца, а не четыре. Я опубликую решение для четырех (как изначально было предложено), но вы можете удалить ненужные столбцы.   -  person steveb    schedule 18.08.2016


Ответы (2)


Примечание: первые ответы относятся к первоначальной задаче вопроса, а последний ответ относится к его текущему состоянию. Данные в data1 должны быть установлены соответствующим образом для каждого решения.

Следующее должно отвечать на ваш первый вопрос с учетом предоставленных вами данных и ожидаемого результата (с использованием dplyr и tidyr).

library(dplyr)
library(tidyr)

### Load Data
data1 <- structure(list(ID = 1:4, Coordinates = c("3438-5150", "3346-5108/3352-5120", 
"2843-4722/3359-5122(1H-2H-3H-4F)", "28.39.97-47.05.62/29.09.13-47.44.03"
)), .Names = c("ID", "Coordinates"), class = "data.frame", row.names = c(NA, 
-4L))

### This is a helper function to transform data that is like '1234'
### but should be '12.34', and leaves alone '12.34'.
### You may have to change this based on your use case.
div100 <- function(x) { return(ifelse(x > 100, x / 100, x)) }

### Remove items like "(...)" and change "12.34.56" to "12.34"
### Split into 4 columns and xform numeric value.
data1 %>%
    mutate(Coordinates = gsub('\\([^)]+\\)', '', Coordinates),
           Coordinates = gsub('(\\d+[.]\\d+)[.]\\d+', '\\1', Coordinates)) %>%
    separate(Coordinates, c('x.1', 'y.1', 'x.2', 'y.2'), fill = 'right', sep = '[-/]', convert = TRUE) %>%
    mutate_at(vars(matches('^[xy][.]')), div100) # xform columns x.N and y.N
##   ID   x.1   y.1   x.2   y.2
## 1  1 34.38 51.50    NA    NA
## 2  2 33.46 51.08 33.52 51.20
## 3  3 28.43 47.22 33.59 51.22
## 4  4 28.39 47.05 29.09 47.44

Вызов mutate дважды изменяет Coordinates, чтобы упростить замену.

Изменить

Вариант, в котором вместо mutate_at используется другая подстановка регулярного выражения.

data1 %>%
mutate(Coordinates = gsub('\\([^)]+\\)', '', Coordinates),
       Coordinates = gsub('(\\d{2}[.]\\d{2})[.]\\d{2}', '\\1', Coordinates),
       Coordinates = gsub('(\\d{2})(\\d{2})', '\\1.\\2', Coordinates)) %>%
separate(Coordinates, c('x.1', 'y.1', 'x.2', 'y.2'), fill = 'right', sep = '[-/]', convert = TRUE)

Изменить 2. Следующее решение касается обновленной версии вопроса

Следующее решение выполняет ряд преобразований для преобразования данных. Они отделены друг от друга, чтобы об этом было немного легче думать (относительно проще говоря).

library(dplyr)
library(tidyr)

data1 <- structure(list(ID = 1:5, Coordinates = c("3438-5150", "3346-5108/3352-5120 East island, South port", 
"East coast (284312 472254)", "28.39.97-47.05.62/29.09.13-47.44.03", 
"2843-4722/3359-5122(1H-2H-3H-4F)")), .Names = c("ID", "Coordinates"
), class = "data.frame", row.names = c(NA, -5L))

### Function for converting to numeric values and
### handles case of "12.34.56" (hours/min/sec)
hms_convert <- function(llval) {
  nres <- rep(0, length(llval))
  coord3_match_idx <- grepl('^\\d{2}[.]\\d{2}[.]\\d{2}$', llval)
  nres[coord3_match_idx] <- sapply(str_split(llval[coord3_match_idx], '[.]', 3), function(x) { sum(as.numeric(x) / c(1,60,3600))})
  nres[!coord3_match_idx] <- as.numeric(llval[!coord3_match_idx])
  nres
}

### Each mutate works to transform the various data formats
### into a single format.  The 'separate' commands then split
### the data into the appropriate columns.  The action of each
### 'mutate' can be seen by progressively viewing the results
### (i.e. adding one 'mutate' command at a time).
data1 %>%
  mutate(Coordinates_new = Coordinates) %>%
  mutate(Coordinates_new = gsub('\\([^) ]+\\)', '', Coordinates_new)) %>%
  mutate(Coordinates_new = gsub('(.*?)\\(((\\d{6})[ ](\\d{6}))\\).*', '\\3-\\4 \\1', Coordinates_new)) %>%
  mutate(Coordinates_new = gsub('(\\d{2})(\\d{2})(\\d{2})', '\\1.\\2.\\3', Coordinates_new)) %>%
  mutate(Coordinates_new = gsub('(\\S+)[\\s]+(.+)', '\\1|\\2', Coordinates_new, perl = TRUE)) %>%
  separate(Coordinates_new, c('Coords', 'label'), fill = 'right', sep = '[|]', convert = TRUE) %>%
  mutate(Coords = gsub('(\\d{2})(\\d{2})', '\\1.\\2', Coords)) %>%
  separate(Coords, c('x.1', 'y.1', 'x.2', 'y.2'), fill = 'right', sep = '[-/]', convert = TRUE) %>%
  mutate_at(vars(matches('^[xy][.]')), hms_convert) %>%
  mutate_at(vars(matches('^[xy][.]')), function(x) ifelse(!is.na(x), -x, x))

##   ID                                 Coordinates       x.1       y.1       x.2       y.2                   label
## 1  1                                   3438-5150 -34.38000 -51.50000        NA        NA                    <NA>
## 2  2 3346-5108/3352-5120 East island, South port -33.46000 -51.08000 -33.52000 -51.20000 East island, South port
## 3  3                  East coast (284312 472254) -28.72000 -47.38167        NA        NA             East coast 
## 4  4         28.39.97-47.05.62/29.09.13-47.44.03 -28.67694 -47.10056 -29.15361 -47.73417                    <NA>
## 5  5            2843-4722/3359-5122(1H-2H-3H-4F) -28.43000 -47.22000 -33.59000 -51.22000                    <NA>
person steveb    schedule 18.08.2016
comment
Огромное спасибо за помощь. Решение мне очень понравилось. Я просто думаю, что мне нужно будет сделать в два этапа. Поскольку у меня есть некоторые координаты 28.39.97-47.05.62 (широта 28º 39 '97' 'долгота 47º 05' 62 ''), мне нужно сначала решить эту проблему. Мне нужно будет извлечь эти данные и преобразовать их в десятичную степень, поскольку большинство значений (28º 39 '97' '- ›это 28,6769, а не 28,39). Тогда я могу безопасно извлечь все координаты в том же формате. Грязный, да? - person aoceano; 18.08.2016
comment
@aoceano Если вы снова решите изменить ввод, задайте новый вопрос. Работа с такой очисткой данных может занять много времени. - person steveb; 18.08.2016
comment
Спасибо большое, не могу выразить себя. В решении есть все, что мне нужно, и то, как вы сформулировали, я могу легко понять и обдумать. Мы уже сделали много заметок о пошаговом процессе и будем использовать некоторые из них для решения нескольких других проблем. Я обязательно уделю этому вопросу больше внимания, чтобы мне не пришлось редактировать проблему, чтобы всем было лучше. Еще раз спасибо за то, что нашли время помочь таким людям, как я, и многим другим. С наилучшими пожеланиями. - person aoceano; 18.08.2016

Мы можем использовать stringi. Мы создаем . между 4-значными числами с помощью gsub, используем stri_extract_all (от stringi) для извлечения двухзначных чисел, за которыми следует точка, за которой следуют двухзначные числа (\\d{2}\\.\\d{2}), чтобы получить результат list. Поскольку элементы list имеют неравную длину, мы можем дополнить NA в конце для тех элементов, длина которых меньше максимальной, и преобразовать их в matrix (используя stri_list2matrix). После преобразования в data.frame изменение столбцов character на numeric и cbind со столбцом «ID» исходного набора данных.

library(stringi)
d1 <- as.data.frame(stri_list2matrix(stri_extract_all_regex(gsub("(\\d{2})(\\d{2})", 
  "\\1.\\2", data1$Coordinates), "\\d{2}\\.\\d{2}"), byrow=TRUE), stringsAsFactors=FALSE)
d1[] <- lapply(d1, as.numeric)
colnames(d1) <-  paste0(c("x.", "y."), rep(1:2,each = 2))

cbind(data1[1], d1)
#  ID   x.1   y.1   x.2   y.2
#1  1 34.38 51.50    NA    NA
#2  2 33.46 51.08 33.52 51.20
#3  3 28.43 47.22 33.59 51.22
#4  4 28.39 47.05 29.09 47.44

Но это также можно сделать с помощью base R.

#Create the dots for the 4-digit numbers
str1 <- gsub("(\\d{2})(\\d{2})", "\\1.\\2", data1$Coordinates)
#extract the numbers in a list with gregexpr/regmatches
lst <- regmatches(str1, gregexpr("\\d{2}\\.\\d{2}", str1))
#convert to numeric
lst <- lapply(lst, as.numeric)
#pad with NA's at the end and convert to data.frame
d1 <- do.call(rbind.data.frame, lapply(lst, `length<-`, max(lengths(lst))))
#change the column names
colnames(d1) <-  paste0(c("x.", "y."), rep(1:2,each = 2))
#cbind with the first column of 'data1'
cbind(data1[1], d1)
person akrun    schedule 18.08.2016
comment
Огромное спасибо за помощь. Я понял, что ты сделал. Я думаю, что мой вопрос был не очень ясен по поводу проблем. Я изменил вопрос и добавил лучший пример. Я пробовал код и все еще пытаюсь заставить его работать в моем новом сценарии. Любые мысли были бы более чем приветствуются. С Уважением. - person aoceano; 18.08.2016
comment
@aoceano Мне очень жаль, что решение было основано на ваших первоначальных предложениях. Если вы скажете, что это не ваш вклад, это будет напрасная трата времени. - person akrun; 18.08.2016
comment
Мне очень жаль, что я зря потратил ваше время. Я очень ценю то, что люди здесь делают, и не тороплюсь, чтобы помочь другим. Моя ошибка заключалась в том, чтобы не прояснить лучше и не предоставить лучший пример моей проблемы. Тем не менее, я многому научился из вашего ответа и буду использовать в других случаях в своей работе, и это поможет улучшить мои знания R. Еще раз спасибо, очень жаль. - person aoceano; 18.08.2016