【问题标题】:Getting football(soccer) stats with R使用 R 获取足球(足球)统计数据
【发布时间】:2016-01-03 22:45:38
【问题描述】:

TL;DR:这篇文章的大部分内容都包含我为尽可能清晰而包含的示例,但问题的核心包含在中间部分“实际问题”中,其中示例被简化为骨骼.

我的问题:

我有一个数据库,其中包含有关足球比赛的数据,我试图从中提取一些统计数据。
该数据库仅包含一个名为“allMatches”的表,其中每个条目表示一个匹配项,该表的字段(我只是包括绝对必要的字段以说明问题所在)是:

  • ID:int,表的主键
  • 日期:日期,比赛进行的日期
  • HT:varchar,主队
  • AT:varchar,客队
  • HG:int,主队得分
  • AG:int,客队得分

对于数据库中的每个条目,我必须提取一些关于客队和主队的统计数据。当您考虑所有先前比赛的统计数据时,这可以很容易地实现,例如,要获得进球和失球统计数据,首先我运行这个查询:

singleTeamAllMatches=
select ID as MatchID, 
       Date as Date,  
       HT as Team,  
       HG as Scored,  
       AG as Conceded
from allMatches  
UNION ALL  
select ID as MatchID, 
       Date as Date,  
       AT as Team,  
       AG as Scored,  
       HG as Conceded
from allMatches;

这不是绝对必要的,因为它只是以这种方式转换原始表:

this row in allMatches:    
|ID |Date       | HT   |AT        |HG | AG|
|42 |2011-05-08 |Genoa |Sampdoria | 2 | 1 |

"becomes" two rows in singleTeamAllMatches:
|MatchID |Date       |Team      |Scored|Conceded|
|42      |2011-05-08 |Genoa     | 2    | 1      |
|42      |2011-05-08 |Sampdoria | 1    | 2      |

但允许我通过一个非常简单的查询获得所需的统计数据:

select a.MatchID as MatchID,
       a.Team as Team,
       Sum(b.Scored) as totalScored,
       Sum(b.Conceded) as totalConceded
from singleTeamAllMatches a, singleTeamAllMatches b
where a.Team == b.Team AND b.Date < a.Date

我最终得到一个查询,运行时返回:

  • MatchID:原数据库中对应匹配的ID
  • 团队:该行数据所属的团队
  • totalScored:球队在 ID 指示的比赛之前的所有比赛中的进球数
  • totalConceded:球队在 ID 所指示的那一场之前的所有比赛中的进球数

换句话说,如果在最后一个查询中我得到:

|MatchID| Team      |totalScored|totalConceded|
|42     | Genoa     |38         | 40          |
|42     | Sampdoria |30         | 42          |

这意味着热那亚和桑普多利亚在ID为42的比赛中交手,而在那场比赛之前热那亚打进38球丢40球,而桑普多利亚打进30球丢42球。

实际问题:

现在,这很容易,因为我考虑了所有之前的比赛,我不知道如何完成的是如何仅考虑之前的 6 场比赛来获得完全相同的统计数据。例如,假设在 singleTeamAllMatches 我有:

|MatchID |Date       |Team      |Scored|Conceded|
|1       |2011-05-08 |TeamA     | 1    | 5      |
|2       |2011-06-08 |TeamA     | 0    | 2      |
|3       |2011-07-08 |TeamA     | 3    | 0      |
|4       |2011-08-08 |TeamA     | 4    | 0      |
|5       |2011-09-08 |TeamA     | 1    | 0      |
|6       |2011-10-08 |TeamA     | 0    | 1      |
|7       |2011-11-08 |TeamA     | 0    | 1      |
|8       |2011-12-08 |TeamA     | 1    | 1      |

我需要想办法获得这样的东西:

|MatchID| Team      |totalScored|totalConceded|
|1      | TeamA     |0          |  0          |
|2      | TeamA     |1          |  5          |
|3      | TeamA     |1          |  7          |
|4      | TeamA     |4          |  7          |
|5      | TeamA     |8          |  7          |
|6      | TeamA     |9          |  7          |
|7      | TeamA     |9          |  8          |
|8      | TeamA     |8          |  4          |

让我们看看这个查询的最后两行:
第 7 行表示在第 7 场比赛(第 1-6 场)之前的最后 6 场比赛中,A 队进 9 球,失 8 球。
第 8 行不受第 1 场比赛进球的影响,因为它只是告诉我们,在第 8 场比赛之前的最后 6 场比赛(第 2-7 场比赛)中,球队 A 进了 8 球,丢了 4 球。
有没有办法通过 sqldf 包用 sql 获得这个? (编辑:实际上任何解决方案都可以,使用 dplyr 包,任务几乎是微不足道的并且有效地完成)

我做了什么以及为什么我不喜欢它

目前我唯一能想到的就是在 R 中导入数据并使用 sql 'LIMIT' 和 sqldf R 包遍历 allMatches 中的所有行。
以下是对我在此处使用的代码示例的改编。这只是一个示例,仅获取主队的统计数据,但完整的代码很长,在这里没有用处。
allMatches 和 singleTeamAllMatches 是数据帧,其结构和内容与我上面描述的表和查询相同。

lastMatchesData <- NULL
for(match in (1:nrow(allMatches))){
  matchRow <- allMatches[match,]
  T <- matchRow$HT
  Date <- matchRow$Date
  ID <- matchRow$ID
  lastMatches <- singleTeamAllMatches[singleTeamAllMatches$T == T & singleTeamAllMatches$Date < Date ,]
  TPerformance <- sqldf("select sum(Scored) as Scored,
                                sum(Conceded) as Conceded
                         from 
                         (select * from lastMatches order by Date DESC limit 6)")
  newRow <- cbind(ID,TPerformance)
  lastMatchesData <- rbind(lastMatchesData,newRow)
}

我不喜欢这个解决方案有两个原因:首先,它真的很丑很乱,记住这只是一个示例,但我想我将来会修改这个代码,并且全sql解决方案会好得多。 第二个原因是它很慢,我的意思是真的很慢,再次使用全 sql 解决方案会好得多。

【问题讨论】:

  • 您使用的是哪个 DBMS?后格雷斯?甲骨文?
  • @a_horse_with_no_neme 这是 R 项目的一部分,所以我只使用 R 包 sqldf,而后者又使用 SQLite。事实是,由于我必须使用我在帖子中描述的解决方法,所以不导出数据以稍后重新导入它们对我来说是有意义的。我也尝试过使用mysql,但没有结果。

标签: sql r limit dplyr


【解决方案1】:

这是我使用 dplyr 提出的一种解决方案:

library(dplyr)
df <- df %>% group_by(Team) %>% mutate(cumScored = cumsum(Scored), totalScored = cumScored - ifelse(row_number() >= 7, lag(cumScored, 6), 0), cumConceded = cumsum(Conceded), totalConceded = cumConceded - ifelse(row_number() >= 7, lag(cumConceded, 6), 0)) %>% select(-cumScored, -cumConceded)

这个想法是首先计算得分和让步的累积总和,然后只保留最后六场比赛,从当前累积总和中减去累积总和的第 6 个滞后,这样你就得到最后六场的部分累积总和比赛滞后。我无法找到一种方法在任意数量的滞后时间上进行累积总和。因此,使用添加新列然后取消选择它的技巧。希望这会有所帮助。

【讨论】:

  • 谢谢,我现在确实意识到我的问题不是很好,因为我专门要求使用 SQL 的解决方案,而我真正考虑的是任何解决方案,我会说这个包对我的项目来说是一个更好的工具。
【解决方案2】:

考虑correlated aggregate subqueries 的每个totalScoredtotalConceded 字段,以最后6 场比赛为条件。在聚合查询中使用派生表子查询时检查其性能。

SELECT t1.Date, t1.MatchID, t1.Team, 
       (SELECT Sum(t2.Scored) 
         FROM  (SELECT t2sub.MatchID, t2sub.Team, t2sub.Scored
                FROM singleTeamAllMatches t2sub
                WHERE t2sub.Team = t1.Team
                  AND t2sub.Date < t1.Date
                ORDER BY t2sub.Date DESC
                LIMIT 6) As t2
       ) As totalScored,

       (SELECT Sum(t3.Conceded) 
         FROM  (SELECT t3sub.MatchID, t3sub.Team, t3sub.Conceded
                FROM singleTeamAllMatches t3sub
                WHERE t3sub.Team = t1.Team
                  AND t3sub.Date < t1.Date
                ORDER BY t3sub.Date DESC
                LIMIT 6) As t3
       ) As totalConceded

FROM singleTeamAllMatches t1

【讨论】:

    【解决方案3】:

    如果你不是特别想使用R。这在MS SQL 中使用PARTITION 很容易实现。

    所以,你可以这样做:

    SELECT MatchID, Team,  
     ISNULL(SUM(Scored) OVER 
     (PARTITION BY Team ORDER BY MatchID ROWS 
      BETWEEN 6 PRECEDING AND 1 PRECEDING),0) as TotalScored,
     ISNULL(SUM(Conceded) OVER (PARTITION BY Team ORDER BY MatchID ROWS 
      BETWEEN 6 PRECEDING AND 1 PRECEDING),0) as TotalConceded
    FROM singleTeamAllMatches
    

    检查结果 here 是否与您想要的输出相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多