如何正确从 API 端点加载 JSON 数据作为 DataFrame

问题描述 投票:0回答:1

我正在开发一个 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
    }
}
swift coreml createml
1个回答
0
投票

由于错误指出

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)
}
© www.soinside.com 2019 - 2024. All rights reserved.