R:如何将PGN作为数据框读入

时间:2015-05-19 22:34:46

标签: r

我有大量国际象棋游戏的单个.pgn Portable Game Notation。游戏包含在文件中,如下所示:

[Event "4th Bayern-chI Bank Hofmann"]
[Site "?"]
[Date "2000.10.29"]
[Round "?"]
[White "Carlsen, Magnus"]
[Black "Cordts, Ingo"]
[ECO "A56"]
[WhiteElo "0"]
[BlackElo "2222"]
[Result "0-1"]

1. d4 Nf6 2. c4 c5 3. Nf3 cxd4 4. Nxd4 e5 5. Nb5 d5 6. cxd5 Bc5 7. N5c3 O-O 8. e3 e4 9. h3 Re8 10. g4 Re5 11. Bc4 Nbd7 12. Qb3 Ne8 13. Nd2 Nd6 14. Be2 Qh4 15. Nc4 Nxc4 16. Qxc4 b5 17. Qxb5 Rb8 18. Qa4 Nf6 19. Qc6 Nd7 20. d6 Re6 21. Nxe4 Bb7 22. Qxd7 Bxe4 23. Rh2 Bxd6 24. Bc4 Rd8 25. Qxa7 Bxh2 26. Bxe6 fxe6 27. Qa6 Bf3 28. Bd2 Qxh3 29. Qxe6+ Kh8 30. Qe7 Bc7 

0-1


[Event "4th Bayern-chI Bank Hofmann"]
[Site "?"]
[Date "2000.10.30"]
[Round "?"]
[White "Kaiser, Guenter"]
[Black "Carlsen, Magnus"]
[ECO "A46"]
[WhiteElo "0"]
[BlackElo "0"]
[Result "0-1"]

1. d4 Nf6 2. Nf3 d6 3. Nc3 g6 4. e4 Bg7 5. Be2 O-O 6. O-O e5 7. Be3 h6 8. Qd2 Ng4 9. d5 f5 10. exf5 gxf5 11. h3 Nxe3 12. Qxe3 e4 13. Nd4 Qe7 14. Rad1 c5 15. dxc6 bxc6 16. Bc4+ Kh7 17. Nce2 d5 18. Bb3 c5 19. Nb5 d4 20. Qd2 Bb7 21. Nf4 a6 22. Nd5 Qe5 23. Nbc7 Ra7 24. Qa5 Nd7 25. g3 Rc8 26. Nb5 Raa8 27. Nbc7 Bxd5 28. Nxa8 Rxa8 29. Ba4 Be6 30. Kh2 f4 31. Qe1 Nf6 32. Bc6 Rc8 33. Bb7 Rc7 34. Ba8 Bd5 35. Bxd5 Nxd5 36. Qe2 fxg3+ 

0-1

我想将此数据作为DataFrame读取,其中列标题只是引号中字符串左侧的单词,行值是引号中的任何内容。另一列将包含所有移动的字符串。

我对R来说是全新的,根本无法弄清楚如何读取尚未采用某种已知格式的文件。

readLines()看起来很有希望。

2 个答案:

答案 0 :(得分:8)

试试这个:

pgn <- read.table("your_file.pgn", quote="", sep="\n", stringsAsFactors=FALSE)

# get column names
colnms <- sub("\\[(\\w+).+", "\\1", pgn[1:12,1])
# give columns 11 (the moves) and 12 (redundant results column) nice names
colnms[11] <- "Moves"
colnms[12] <- "Results2"

pgn.df <- data.frame(matrix(sub("\\[\\w+ \\\"(.+)\\\"\\]", "\\1", pgn[,1]),
                     byrow=TRUE, ncol=12))

names(pgn.df) <- colnms

此解决方案假设每个游戏都是12行,如您的示例所示。如果游戏占用了不同数量的线,那么这个解决方案就无法工作。

正则表达式行的说明(更多信息见?regex):

sub("\\[(\\w+).+", "\\1", pgn[1:12,1])

在这个正则表达式中,我们想要一个方括号后的第一个单词。我们必须逃避这个支架,因为它是一个元字符。还有其他方法可以在不使用转义符(\)的情况下实现这一点,例如将[放在方括号内sub("[[](\\w+).+", "\\1", pgn[1:12,1])\\1

括号(捕获组)与\\1一起使用。 sub作为\\2的第二个参数表示将原始字符串替换为第一个(仅在此情况下)捕获组的内容。如果有第二个捕获组,您可以使用\\w+来引用它。

捕获组+的内容是一个或多个(\\w表示的)单词字符(由()表示)。在.之后我们想要匹配字符串的其余部分,我们可以通过查找任何字符(“.+的含义”)来执行一次或多次(即{{1} })。

因此,正则表达式找到第一个方括号和我们捕获的第一个连续的单词字符块,后跟一个或多个任何其他字符。

第二个正则表达式:"\\[\\w+ \\\"(.+)\\\"\\]"

让我们看看pgn[,1][1] "[Event \"4th Bayern-chI Bank Hofmann\"]"的第一个条目。我们的开头与第一个正则表达式相同,但这次我们不想捕获第一个单词,我们只想找到它后跟一个空格,然后我们想要捕获两组{之间的所有内容{1}}。

必须对\"\进行转义,因此我们在一个捕获组周围有一对",可以一次或多次查找任何字符(\\\" ),最后我们有一个方括号,我们以与第一个方括号相同的方式逃脱。如果我们没有逃脱.+,R会认为这是"的第一个参数的结束,而不是将sub解释为字面引用。

对于第11行和第12行等条目,没有任何内容匹配,因为两行都不以"开头,因此,没有任何内容可以替代。我们只是将原始字符串整理回来。

答案 1 :(得分:3)

这是我尝试的内容:

con = file("pgn_file.txt", "r")
all_lines = readLines(con)
close(con);

res = list();
for(this_line in all_lines)
  {
  if(grepl("^\\s*$", this_line, perl=T))
    {
    print("Empty line: do nothing")
    }else
    {
    if(grepl("^\\[", this_line, perl=T))
      {
      field = gsub("^\\[\\s*([a-zA-Z]+)\\s*\"([a-zA-Z0-9\\s.?, -]+)\"\\]$", "\\1", this_line);
      value = gsub("^\\[\\s*([a-zA-Z]+)\\s*\"([a-zA-Z0-9\\s.?, -]+)\"\\]$", "\\2", this_line);
      print(field);
      res[[tolower(field)]] = c(res[[tolower(field)]], value);
      }else
      {
      print(this_line)
      }
    if(grepl("^1\\.", this_line, perl=T))
      {
      res[["move_list"]] = c(res[["move_list"]], this_line);
      }
    }
  }
res = as.data.frame(res);