Save DataFrames as CSVs

Hi, 

I am relatively new to Swift. 

How do you save DataFrames as CSVs (using the TabularData Framework)? 

For example, having already ‘joined’ two DataFrames on an Xcode Playground, I would like to save that ‘new’ DataFrame as a CSV - locally on my MacBook.

Any example code would be much appreciated.

Thanks

Replies

The TabularData > DataFrame docs have a group, Saving a Data Frame to a CSV Format, that lists the API for this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi

Many thanks for responding. 

Apologies, I appreciate this will be straightforward but I seem to be going wrong with the URL/local URL.

As I say, I am relatively new/inexperienced with Swift - could you please provide some example code to demonstrate how to do this?

I would also like to combine several CSV’s into one big CSV. How can I do this using TabularData?

Thanks 

I seem to be going wrong with the URL/local URL.

Most Apple APIs use file URLs to reference places in the file system. If you just need to cook up a file URL for a quick test, URL(fileURLWithPath:) is your friend:

let url = URL(fileURLWithPath: "/Users/quinn/test.csv")

If you’re building real code things get more complex because you have to choose where to save your file, which is platform specific. If that’s the case, let me know what platform you’re targeting.

I would also like to combine several CSV’s into one big CSV. How can I do this using TabularData?

Use the append(_:) method to add one frame to the other and then save the complete frame. See the example below.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


import Foundation
import TabularData

let novels60s = """
    "Title","Year"
    "Rocannon's World","1966"
    "Planet of Exile","1966"
    "City of Illusions","1967"
    "A Wizard of Earthsea","1968"
    "The Left Hand of Darkness","1969"
    """

let novels70s = """
    "Title","Year"
    "The Tombs of Atuan","1971"
    "The Lathe of Heaven","1971"
    "The Farthest Shore","1972"
    "The Dispossessed","1974"
    "Very Far Away from Anywhere Else","1976"
    "The Eye of the Heron","1978"
    "Malafrena","1979"
    """

func mergeCSV() throws {
    var df = try DataFrame(csvData: Data(novels60s.utf8))
    let df70s = try DataFrame(csvData: Data(novels70s.utf8))
    df.append(df70s)
    let result = try df.csvRepresentation()
    print(String(decoding: result, as: UTF8.self))
}

/*

Title,Year
Rocannon's World,1966
Planet of Exile,1966
City of Illusions,1967
A Wizard of Earthsea,1968
The Left Hand of Darkness,1969
The Tombs of Atuan,1971
The Lathe of Heaven,1971
The Farthest Shore,1972
The Dispossessed,1974
Very Far Away from Anywhere Else,1976
The Eye of the Heron,1978
Malafrena,1979

 */

try! mergeCSV()

Thanks very much. That was really helpful. 

(For awareness, I received an error for a particular folder - Error Domain=NSPOSIXErrorDomain Code=2 “No such file or directory”, which threw me slightly but the code worked fine on other folders.) 

As you mentioned, URL(fileURLWithPath:) was useful for a quick test. I’d also be very grateful for specific guidance on targeting iOS.

Re merging CSVs/the append method.

Thanks again. I can now merge CSVs. However, my current code is not very efficient. For context, I am looking to merge over 40 CSVs.

Is there an efficient way to merge all the CSV files within a folder?

Thanks 

I’d also be very grateful for specific guidance on targeting iOS.

The best approach for iOS depends on the user interface you want to display. What do you expect that to look like? Where do these CSV files come from? And where is the final result going?

Is there an efficient way to merge all the CSV files within a folder?

There are two parts to this:

  1. Getting a list of file URLs for every CSV file in a folder.

  2. Merging all the CSV files at those URLs.

For the first part, you have two options:

In both cases you’ll want to filter the list based on the URL’s pathExtension property.

For the second part, you get the first URL from your list and import it. Then write a loop that iterates over the rest of the URLs in the list, importing and merging each one in turn.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks very much.

Re merging all the CSVs in a folder, I managed to get this to work some of the time. However, in certain situations (when testing a different subset of the CSVs), I encountered the following error (on a Xcode Playground).

"error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation."

If it is possible to see some example code (and receive guidance on the above error) to help, that would be much appreciated.

I also noticed the following warning:

'init(fileURLWithPath:)' will be deprecated in a future version of macOS. Use init(filePath:directoryHint:relativeTo:) instead.

As such, I used filePath rather than fileURLWithPath.

Thanks again.

I encountered the following error

This typically means that your program trapped, that is, some code within the program detected a problem and deliberately crashed it. You see this, for example, if you unwrap an Optional that’s nil or access an array element out of bounds.

As to what’s causing this in your case, it’s hard to say. In a normal Xcode project you could look at the backtrace within the debugger, but Xcode playgrounds don’t support the debugger. That’s one of the reasons I do this sort of work in a regular Xcode project, created from the macOS > Command Line Tool template.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi,

As a follow on to the above, when 'joining' DataFrames and writing to CSV - how do you remove the prefixes left and right?

Thanks

I don’t think there’s a way to do that but, if you know that the names are unique, you can rename the columns after the join.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks.