How to get the output of a for-in loop into a single DataFrame instead of many Dataframes

I am trying to get the data for each track in a MusicItemCollection into a DataFrame. The only way I know to go about this is to use a for-in loop, but this creates a DataFrame for each track instead of a DataFrame for all of the tracks. My code is:

let albumTracks = album?.tracks
for tracks in albumTracks {

     let dataFrame: DataFrame = [

         "track": [tracks.trackNumber],

         "title": [tracks.title],

         "artist": [tracks.artistName],

         "release date": [tracks.releaseDate?.formatted(date: .long, time: .omitted) ?? "not available"],

         "duration": [tracks.duration],

         "isrc": [tracks.isrc]

         ]

     print(dataFrame)

I have also tried

 for tracks in self.albumTracks {

     var dataFrame = DataFrame.init()

     let trackNumColumn = Column.init(name: "track", contents: [String(tracks.trackNumber!)])

     dataFrame.append(column: trackNumColumn)

     let titleColumn = Column.init(name: "title", contents: [tracks.title])

     dataFrame.append(column: titleColumn)

     let artistColumn = Column.init(name: "artist", contents: [tracks.artistName])

     dataFrame.append(column: artistColumn)

     let releaseDateColumn = Column.init(name: "release date", contents: [tracks.releaseDate?.formatted(date: .long, time: .omitted)])

     dataFrame.append(column: releaseDateColumn)

     let idColumn = Column.init(name: "id", contents: [String(tracks.id.rawValue)])

     dataFrame.append(column: idColumn)

     

     print(dataFrame)

Both of these methods work outside of a loop according to the tech talks video: https://developer.apple.com/videos/play/tech-talks/10100/ , but I cannot figure out how to call the individual tracks outside of a loop

Thanks

Answered by Frameworks Engineer in 700050022

Hello @AnimalOnDrums,

While you could use a for loop to append elements to your Column instances one by one, you might prefer to leverage the fact that Album's tracks are returned as a MusicItemCollection, which like most collections has the map(_:) method.

let albumTracks = album.tracks ?? []

var dataFrame = DataFrame()

let trackNumberColumn = Column(name: "track", contents: albumTracks.map(\.trackNumber))
dataFrame.append(column: trackNumberColumn)

let titleColumn = Column(name: "title", contents: albumTracks.map(\.title))
dataFrame.append(column: titleColumn)

let artistColumn = Column(name: "artist", contents: albumTracks.map(\.artistName))
dataFrame.append(column: artistColumn)

let releaseDateColumn = Column(name: "release date", contents: albumTracks.map { track in
    return track.releaseDate?.formatted(date: .long, time: .omitted)
})
dataFrame.append(column: releaseDateColumn)

let durationColumn = Column(name: "duration", contents: albumTracks.map(\.duration))
dataFrame.append(column: durationColumn)

let idColumn = Column(name: "id", contents: albumTracks.map(\.id.rawValue))
dataFrame.append(column: idColumn)

print("\(dataFrame)")

Here's what I see when I run this code:

┏━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓
┃    ┃ track ┃ title                         ┃ artist    ┃ release date      ┃ duration ┃ id        ┃
┃    ┃ <Int> ┃ <String>                      ┃ <String>  ┃ <String>          ┃ <Double> ┃ <String>  ┃
┡━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━┩
│ 0  │     1 │ One More Time                 │ Daft Punk │ November 29, 2000 │  320.357 │ 697195462 │
│ 1  │     2 │ Aerodynamic                   │ Daft Punk │ February 25, 2001 │  212.547 │ 697195570 │
│ 2  │     3 │ Digital Love                  │ Daft Punk │ February 25, 2001 │  301.373 │ 697195633 │
│ 3  │     4 │ Harder Better Faster Stronger │ Daft Punk │ February 25, 2001 │  224.693 │ 697195787 │
│ 4  │     5 │ Crescendolls                  │ Daft Punk │ February 25, 2001 │   211.64 │ 697195813 │
│ 5  │     6 │ Nightvision                   │ Daft Punk │ February 25, 2001 │  104.467 │ 697195819 │
│ 6  │     7 │ Superheroes                   │ Daft Punk │ February 25, 2001 │    237.8 │ 697195890 │
│ 7  │     8 │ High Life                     │ Daft Punk │ February 25, 2001 │    201.8 │ 697195929 │
│ 8  │     9 │ Something About Us            │ Daft Punk │ February 25, 2001 │  232.667 │ 697195941 │
│ 9  │    10 │ Voyager                       │ Daft Punk │ February 25, 2001 │  227.867 │ 697195991 │
│ 10 │    11 │ Veridis Quo                   │ Daft Punk │ February 25, 2001 │  345.187 │ 697196125 │
│ 11 │    12 │ Short Circuit                 │ Daft Punk │ February 25, 2001 │  206.867 │ 697196212 │
│ 12 │    13 │ Face to Face                  │ Daft Punk │ February 25, 2001 │  240.173 │ 697196420 │
│ 13 │    14 │ Too Long                      │ Daft Punk │ February 25, 2001 │  600.293 │ 697196490 │
└────┴───────┴───────────────────────────────┴───────────┴───────────────────┴──────────┴───────────┘
14 rows, 6 columns

I hope this helps.

Best regards,

Accepted Answer

Hello @AnimalOnDrums,

While you could use a for loop to append elements to your Column instances one by one, you might prefer to leverage the fact that Album's tracks are returned as a MusicItemCollection, which like most collections has the map(_:) method.

let albumTracks = album.tracks ?? []

var dataFrame = DataFrame()

let trackNumberColumn = Column(name: "track", contents: albumTracks.map(\.trackNumber))
dataFrame.append(column: trackNumberColumn)

let titleColumn = Column(name: "title", contents: albumTracks.map(\.title))
dataFrame.append(column: titleColumn)

let artistColumn = Column(name: "artist", contents: albumTracks.map(\.artistName))
dataFrame.append(column: artistColumn)

let releaseDateColumn = Column(name: "release date", contents: albumTracks.map { track in
    return track.releaseDate?.formatted(date: .long, time: .omitted)
})
dataFrame.append(column: releaseDateColumn)

let durationColumn = Column(name: "duration", contents: albumTracks.map(\.duration))
dataFrame.append(column: durationColumn)

let idColumn = Column(name: "id", contents: albumTracks.map(\.id.rawValue))
dataFrame.append(column: idColumn)

print("\(dataFrame)")

Here's what I see when I run this code:

┏━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓
┃    ┃ track ┃ title                         ┃ artist    ┃ release date      ┃ duration ┃ id        ┃
┃    ┃ <Int> ┃ <String>                      ┃ <String>  ┃ <String>          ┃ <Double> ┃ <String>  ┃
┡━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━┩
│ 0  │     1 │ One More Time                 │ Daft Punk │ November 29, 2000 │  320.357 │ 697195462 │
│ 1  │     2 │ Aerodynamic                   │ Daft Punk │ February 25, 2001 │  212.547 │ 697195570 │
│ 2  │     3 │ Digital Love                  │ Daft Punk │ February 25, 2001 │  301.373 │ 697195633 │
│ 3  │     4 │ Harder Better Faster Stronger │ Daft Punk │ February 25, 2001 │  224.693 │ 697195787 │
│ 4  │     5 │ Crescendolls                  │ Daft Punk │ February 25, 2001 │   211.64 │ 697195813 │
│ 5  │     6 │ Nightvision                   │ Daft Punk │ February 25, 2001 │  104.467 │ 697195819 │
│ 6  │     7 │ Superheroes                   │ Daft Punk │ February 25, 2001 │    237.8 │ 697195890 │
│ 7  │     8 │ High Life                     │ Daft Punk │ February 25, 2001 │    201.8 │ 697195929 │
│ 8  │     9 │ Something About Us            │ Daft Punk │ February 25, 2001 │  232.667 │ 697195941 │
│ 9  │    10 │ Voyager                       │ Daft Punk │ February 25, 2001 │  227.867 │ 697195991 │
│ 10 │    11 │ Veridis Quo                   │ Daft Punk │ February 25, 2001 │  345.187 │ 697196125 │
│ 11 │    12 │ Short Circuit                 │ Daft Punk │ February 25, 2001 │  206.867 │ 697196212 │
│ 12 │    13 │ Face to Face                  │ Daft Punk │ February 25, 2001 │  240.173 │ 697196420 │
│ 13 │    14 │ Too Long                      │ Daft Punk │ February 25, 2001 │  600.293 │ 697196490 │
└────┴───────┴───────────────────────────────┴───────────┴───────────────────┴──────────┴───────────┘
14 rows, 6 columns

I hope this helps.

Best regards,

  1. First make your data model conform to Codable
  2. Encode your model (albumtracks) to JSON data
  3. Init DataFrame with JSONData.

Example:


struct Track: Encodable {
    var name: String
    var value: String
}

let tracks = [
Track(name: "Red", value: "Angry birds"), Track(name: "Mighty Eagle", value: "Angry birds #2"), Track(name: "Chuck", value: "Angry birds #3")
]
let json = try? JSONEncoder().encode(tracks)
let dataframe = try? DataFrame(jsonData: json!)
┏━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓

┃   ┃ name         ┃ value          ┃

┃   ┃ <String>     ┃ <String>       ┃

┡━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩

│ 0 │ Red          │ Angry birds    │

│ 1 │ Mighty Eagle │ Angry birds #2 │

│ 2 │ Chuck        │ Angry birds #3 │

└───┴──────────────┴────────────────┘

3 rows, 2 columns

@JoeKun, Thanks so much for the detailed reply! Again, you are a life saver!

@MobileTen, Thanks for help on this too! I had actually tried your method, except that I can't write out the tracks' names and other details since I do not know what is being returned in the MusicItemCollection for each search. It's a good idea and should work, but while the Encoding of MusicItemCollection to a JSON works, when I tried to use the json for the DataFrame I would get errors about the json not being formatted properly because "the top level is not a sequence". Strange, huh?

Happy New Year to you both!

Hello @AnimalOnDrums,

Just a quick update: we have now created a new tag on the Developer Forums for TabularData.

Here is the link for that new TabularData tag.

Feel free to use it with any new thread you start about this framework.

I hope this helps.

Best regards,

How to get the output of a for-in loop into a single DataFrame instead of many Dataframes
 
 
Q