使用 R 获取足球数据
Getting football(soccer) stats with R
TL;DR:这个 post 的大部分内容包含示例,我已将其包括在内以尽可能清楚,但问题的核心包含在中间部分 "The Actual question" 中例子都精简了。
我的问题:
我有一个数据库,其中包含有关足球比赛的数据,我正试图从中提取一些统计数据。
数据库只包含一个 table,称为 'allMatches',其中每个条目代表一个匹配项,字段(我只包括绝对必要的字段,以了解问题所在) table 是:
- ID: int, table
的主键
- Date:日期,比赛开始的日期
- HT:varchar,主队
- AT:varchar,客队
- HG:整数,主队得分
- 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;
这不是绝对必要的,因为它只是简单地把原来的table转换成这样:
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
我最终得到一个查询,当 运行ned 时,returns:
- 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场比赛之前的最后6场比赛中(第1-6场比赛)teamA进了9个球,丢了8个。
第 8 行不受第 1 场进球数的影响,因为它只是告诉我们在第 8 场比赛之前的最后 6 场比赛(第 2-7 场比赛)中,teamA 进了 8 个球,丢了 4 个球。
有没有办法通过 sqldf 包用 sql 获得这个?
(编辑:实际上任何解决方案都可以,使用 dplyr 包任务几乎是微不足道的并且可以高效地完成)
我做了什么以及为什么我不喜欢它
目前我唯一能想到的就是将数据导入 R 并使用 sql 'LIMIT' 和 sqldf 遍历 allMatches 中的所有行R包.
以下是对我在此处使用的代码示例的改编。这只是一个仅获取主队统计数据的示例,但完整的代码很长,在这里不会有用。
allMatches 和 singleTeamAllMatches 是数据帧,其结构和内容与我上面描述的 table 和查询相同。
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解决方案会好得多。
考虑 correlated aggregate subqueries totalScored
和 totalConceded
字段,以最后 6 场比赛为条件。检查派生的性能 table 子查询在聚合查询中使用。
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
这是我使用 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)
思路是先计算得分和让步的累计和,然后只保留最后六场比赛,从当前累计和中减去累计和的第六滞后,得到部分累计和最后六场比赛。我找不到一种方法可以在任意数量的滞后上即时进行累积和。因此,使用添加新列然后取消选择的技巧。希望这有帮助。
如果您不确定是否要使用 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 是否与您想要的输出相同。
TL;DR:这个 post 的大部分内容包含示例,我已将其包括在内以尽可能清楚,但问题的核心包含在中间部分 "The Actual question" 中例子都精简了。
我的问题:
我有一个数据库,其中包含有关足球比赛的数据,我正试图从中提取一些统计数据。
数据库只包含一个 table,称为 'allMatches',其中每个条目代表一个匹配项,字段(我只包括绝对必要的字段,以了解问题所在) table 是:
- ID: int, table 的主键
- Date:日期,比赛开始的日期
- HT:varchar,主队
- AT:varchar,客队
- HG:整数,主队得分
- 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;
这不是绝对必要的,因为它只是简单地把原来的table转换成这样:
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
我最终得到一个查询,当 运行ned 时,returns:
- 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场比赛之前的最后6场比赛中(第1-6场比赛)teamA进了9个球,丢了8个。
第 8 行不受第 1 场进球数的影响,因为它只是告诉我们在第 8 场比赛之前的最后 6 场比赛(第 2-7 场比赛)中,teamA 进了 8 个球,丢了 4 个球。
有没有办法通过 sqldf 包用 sql 获得这个?
(编辑:实际上任何解决方案都可以,使用 dplyr 包任务几乎是微不足道的并且可以高效地完成)
我做了什么以及为什么我不喜欢它
目前我唯一能想到的就是将数据导入 R 并使用 sql 'LIMIT' 和 sqldf 遍历 allMatches 中的所有行R包.
以下是对我在此处使用的代码示例的改编。这只是一个仅获取主队统计数据的示例,但完整的代码很长,在这里不会有用。
allMatches 和 singleTeamAllMatches 是数据帧,其结构和内容与我上面描述的 table 和查询相同。
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解决方案会好得多。
考虑 correlated aggregate subqueries totalScored
和 totalConceded
字段,以最后 6 场比赛为条件。检查派生的性能 table 子查询在聚合查询中使用。
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
这是我使用 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)
思路是先计算得分和让步的累计和,然后只保留最后六场比赛,从当前累计和中减去累计和的第六滞后,得到部分累计和最后六场比赛。我找不到一种方法可以在任意数量的滞后上即时进行累积和。因此,使用添加新列然后取消选择的技巧。希望这有帮助。
如果您不确定是否要使用 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 是否与您想要的输出相同。