# CSV upload

Upload a CSV file of [time-series data](https://dev.nominal.io/platform/data-model.md#data-model-time-series) to a dataset. The timestamp column places each row in time, and every other column becomes a [channel](https://dev.nominal.io/platform/data-model.md#data-model-channels).

## In code

Each example uploads [`sample.csv`](https://dev.nominal.io/quickstart/_downloads/4f1334ef9c0fac171d2efc196d27e9ff/sample.csv), a minute of test data whose `timestamp` column holds ISO 8601 times, to an existing dataset:

**Python**

```python
from nominal.core import NominalClient

client = NominalClient.from_profile("default")
dataset = client.get_dataset("<ri.catalog.instance.dataset.identifier>")  # or client.create_dataset("My dataset")

file = dataset.add_tabular_data("sample.csv", timestamp_column="timestamp", timestamp_type="iso_8601")  # also Parquet
file.poll_until_ingestion_completed()  # raises NominalIngestError if the ingest fails
```

**MATLAB**

```matlab
client = nominal.Client.fromProfile();
dataset = client.datasetByRid("<ri.catalog.instance.dataset.identifier>");

job = client.ingest("sample.csv", TimestampColumn="timestamp", Dataset=dataset);  % or NewDataset="My dataset"
job.wait();  % raises if the ingest fails
```

**LabVIEW**

![LabVIEW example that uploads a CSV file to a new dataset, with labels and properties](https://dev.nominal.io/quickstart/_images/labview-quick-csv.jpg)

**Rust**

```rust
use nominal::core::{CsvIngest, Timestamp};

let client = nominal::NominalClient::from_profile("default")?;
client.ingest()
    .upload_csv("sample.csv", "<ri.catalog.instance.dataset.identifier>", CsvIngest::new(Timestamp::iso8601("timestamp")))
    .await?;
```

**Go**

```go
import (
	"bytes"
	"os"

	ingestapi "github.com/nominal-io/nominal-api-go/ingest/api"
	uploadapi "github.com/nominal-io/nominal-api-go/upload/api"
	"github.com/palantir/conjure-go-runtime/v2/conjure-go-client/httpclient"
	"github.com/palantir/pkg/rid"
	"github.com/palantir/pkg/safelong"
)

// httpClient is a conjure-go-runtime httpclient.Client for your Nominal REST API URL,
// and token is your API key as a bearertoken.Token
data, _ := os.ReadFile("sample.csv")
size := safelong.SafeLong(len(data))
path, _ := uploadapi.NewUploadServiceClient(httpClient).UploadFile(ctx, token, "sample.csv", &size, nil,
	httpclient.RequestBodyInMemory(bytes.NewReader(data)))
datasetRid, _ := rid.ParseRID("<ri.catalog.instance.dataset.identifier>")
ingestapi.NewIngestServiceClient(httpClient).Ingest(ctx, token, ingestapi.IngestRequest{
	Options: ingestapi.NewIngestOptionsFromCsv(ingestapi.CsvOpts{
		Source: ingestapi.NewIngestSourceFromS3(ingestapi.S3IngestSource{Path: string(path)}),
		Target: ingestapi.NewDatasetIngestTargetFromExisting(ingestapi.ExistingDatasetIngestDestination{DatasetRid: datasetRid}),
		TimestampMetadata: ingestapi.TimestampMetadata{
			SeriesName:    "timestamp",
			TimestampType: ingestapi.NewTimestampTypeFromAbsolute(ingestapi.NewAbsoluteTimestampFromIso8601(ingestapi.Iso8601Timestamp{})),
		},
	}),
})
```

**CLI**

```bash
nomctl ingest csv sample.csv --timestamp-column timestamp --timestamp-type iso8601 \
  --dataset <ri.catalog.instance.dataset.identifier>  # or --name "My dataset"
```

## In the Nominal app

1. Open the dataset’s page and click **UPLOAD DATA**. To find the dataset, in the sidebar click **Data**, then **Data sources**.
2. Drop the file onto **Upload files**, or click **SELECT FILES** and pick it.
3. In the **Settings** panel, check the **Timestamp column** and **Timestamp format**. For `sample.csv`, they’re `timestamp` and **Absolute (ISO 8601)**.
4. Click **CONFIRM & INGEST**.
