我正在开发一个 ML 模型来预测英超球队即将举行的比赛,并且我是以编程方式而不是通过 createML 应用程序来进行的。我无法从 API 端点将数据加载为 JSON 数据,然后将其转换为 DataFrame 对象来训练模型,然后预测其游戏。
我当前收到错误:
Unsupported JSON structure. Top-Level object must be sequence
,但如果我在命令行中查询该数据并在 CreateML 应用程序中对其进行训练,它就可以工作。
如果有人可以提供一些有关如何将数据从 API 端点正确加载到 DataFrame 的反馈,我们将不胜感激
代码:
struct MatchPredictionsView: View {
@State var processing: Bool = false
@State var predictions: AnyColumn?
@State var errorMessage: String?
var team: Team?
var body: some View {
VStack {
if processing {
ProgressView()
} else if let predictions = predictions {
// Display predictions
Text("Predictions: \(predictions.description)")
} else if let errorMessage = errorMessage {
Text("Error: \(errorMessage)")
.foregroundColor(.red)
} else {
Text("No predictions available.")
}
}
.task {
await fetchPredictions()
}
}
func fetchPredictions() async {
do {
processing = true
errorMessage = nil
guard let teamIDString = team?.id.description else {
errorMessage = "Invalid team ID."
return
}
predictions = try await predictMatchesEndToEnd(teamID: teamIDString)
} catch {
errorMessage = "Error during match prediction: \(error)"
}
processing = false
}
func predictMatchesEndToEnd(teamID: String) async throws -> AnyColumn {
// API Key and Header
let apiKey = "f169854fa08340abbc130202705c60dc"
let headers = ["X-Auth-Token": apiKey]
// Create JSON URLs
guard let trainingDataURL = URL(
string: "https://api.football-data.org/v4/teams/\(teamID)/matches?dateFrom=2023-08-15&dateTo=2024-05-25"),
let gamesToPredictURL = URL(
string: "https://api.football-data.org/v4/teams/\(teamID)/matches?dateFrom=2024-08-15&dateTo=2025-05-25") else {
throw URLError(.badURL)
}
// Fetch JSON data
let gameTrainingDataJSON = try await fetchJSONData(from: trainingDataURL, headers: headers)
let gamesToPredictJSON = try await fetchJSONData(from: gamesToPredictURL, headers: headers)
let gameTrainingData = try DataFrame(jsonData: gameTrainingDataJSON)
let gamesToPredictDataFrame = try DataFrame(jsonData: gamesToPredictJSON)
// Model Setup
let featureColumns = [
"homeTeam/name",
"awayTeam/name",
"score/fullTime/home",
"score/fullTime/away",
"score/halfTime/home",
"score/halfTime/away",
"referees/0/name",
"season/winner/name"
]
let parameters = MLRandomForestClassifier.ModelParameters(
validation: .split(strategy: .automatic),
maxIterations: 100,
randomSeed: 38
)
let model = try MLRandomForestClassifier(
trainingData: gameTrainingData,
targetColumn: "score/winner",
featureColumns: featureColumns,
parameters: parameters
)
// Predict
let predictions = try model.predictions(from: gamesToPredictDataFrame)
return predictions
}
func fetchJSONData(from url: URL, headers: [String: String]) async throws -> Data {
var request = URLRequest(url: url)
for (key, value) in headers {
request.setValue(value, forHTTPHeaderField: key)
}
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
}
由于错误指出
DataFrame(jsonData:)
需要一个数组作为顶级对象,但接收到的 JSON 是一个字典。您必须提取 matches
数组。
这可以通过使用
JSONSerialization
解码 JSON、获取键 matches
的数组并重新编码 JSON 来完成。
将
fetchJSONData(from:headers:)
替换为
func fetchJSONData(from url: URL, headers: [String: String]) async throws -> Data {
var request = URLRequest(url: url)
for (key, value) in headers {
request.setValue(value, forHTTPHeaderField: key)
}
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
guard let json = try JSONSerialization.jsonObject(with: data) as? [String:Any],
let matches = json["matches"] as? [[String:Any]] else { throw URLError(.cannotDecodeContentData)}
return try JSONSerialization.data(withJSONObject: matches)
}