【问题标题】:R: How to read in a PGN as a Data FrameR:如何将 PGN 作为数据框读取
【发布时间】:2015-07-31 23:17:43
【问题描述】:

我有大量国际象棋游戏的单个 .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() 看起来很有希望。

【问题讨论】:

  • readLines 确实是通常使用的。在读取文件后,您必须进行大量处理才能获得所需的数据帧,我建议将splitting 放入每个元素都是游戏的列表中,编写一个返回适当值的函数每个游戏和lapplying 到你的列表。
  • 太棒了!谢谢@Gregor,我刚刚了解了scan,这很有用,因为我可以跳过空白行,而且看起来要快得多。你知道如何按“行号”分割,这样我读入的对象中的每 12 行就是一个新的游戏元素吗?
  • 当然可以split(raw_data, seq_along(raw_data) %/% 12)

标签: r


【解决方案1】:

试试这个:

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 表示)。在() 之后,我们想要匹配字符串的其余部分,我们可以通过查找任何字符(这就是. 的意思)一次或多次(即.+)来做到这一点。

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

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

我们来看pgn[,1]的第一个条目:[1] "[Event \"4th Bayern-chI Bank Hofmann\"]"。我们开始和第一个正则表达式一样,但是这次我们不想捕获第一个单词,我们只想找到它后面跟一个空格,然后我们想捕获两组\"之间的所有内容.

\" 都必须被转义,所以我们有一对 \\\" 包围一个捕获组,它会查找任何字符一次或多次(.+),最后我们有一个正方形括号,我们以与第一个方括号相同的方式进行转义。如果我们不转义",R 会认为这是sub 的第一个参数的结尾,而不会将" 解释为文字引号。

对于像第 11 行和第 12 行这样的条目,没有任何内容匹配,因为没有一行以 [ 开头,因此没有任何内容被替换。我们只是将原始字符串全部取回。

【讨论】:

  • 这很好用,而且加载速度非常快。你能解释一下这些 Regex 行是如何工作的吗?
  • 当然可以。我将在解释中进行编辑。如果您需要比我给出的更多解释,请告诉我,我会展开。
  • 哇,很棒的解释。非常感谢您花时间写这篇文章。
【解决方案2】:

这是我要尝试的:

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);

【讨论】:

  • 谢谢你;不过,我不得不接受另一个答案,因为它更简洁,而且你在处理我扔给它的几乎所有 .pgn 文件的最后一个元素时都会出错。
猜你喜欢
  • 2018-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
  • 2016-07-27
  • 2013-11-10
相关资源
最近更新 更多