Skip to content
This repository was archived by the owner on Aug 2, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions corefxlab.sln
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.CaseFolding", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Analysis.Interactive", "Microsoft.Data.Analysis.Interactive\Microsoft.Data.Analysis.Interactive.csproj", "{0C11386C-E3CC-45EB-B932-9297A8DD6649}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.Analysis.IO", "src\Microsoft.Data.Analysis.IO\Microsoft.Data.Analysis.IO.csproj", "{BA66674D-CD08-418F-B2F2-04F942108BFB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.Analysis.IO.Tests", "tests\Microsoft.Data.Analysis.IO.Tests\Microsoft.Data.Analysis.IO.Tests\Microsoft.Data.Analysis.IO.Tests.csproj", "{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -753,6 +757,30 @@ Global
{0C11386C-E3CC-45EB-B932-9297A8DD6649}.Release|x64.Build.0 = Release|Any CPU
{0C11386C-E3CC-45EB-B932-9297A8DD6649}.Release|x86.ActiveCfg = Release|Any CPU
{0C11386C-E3CC-45EB-B932-9297A8DD6649}.Release|x86.Build.0 = Release|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Debug|x64.ActiveCfg = Debug|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Debug|x64.Build.0 = Debug|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Debug|x86.ActiveCfg = Debug|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Debug|x86.Build.0 = Debug|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Release|Any CPU.Build.0 = Release|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Release|x64.ActiveCfg = Release|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Release|x64.Build.0 = Release|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Release|x86.ActiveCfg = Release|Any CPU
{BA66674D-CD08-418F-B2F2-04F942108BFB}.Release|x86.Build.0 = Release|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Debug|x64.ActiveCfg = Debug|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Debug|x64.Build.0 = Debug|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Debug|x86.ActiveCfg = Debug|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Debug|x86.Build.0 = Debug|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Release|Any CPU.Build.0 = Release|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Release|x64.ActiveCfg = Release|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Release|x64.Build.0 = Release|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Release|x86.ActiveCfg = Release|Any CPU
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -810,6 +838,8 @@ Global
{6435C875-F526-45E1-97E1-B5BF8C4F9A55} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
{6B8DAAD7-6CC3-4138-B719-02AE81BEC535} = {4B000021-5278-4F2A-B734-DE49F55D4024}
{0C11386C-E3CC-45EB-B932-9297A8DD6649} = {4B000021-5278-4F2A-B734-DE49F55D4024}
{BA66674D-CD08-418F-B2F2-04F942108BFB} = {4B000021-5278-4F2A-B734-DE49F55D4024}
{872F67A5-91EA-4A82-8626-C0D2DCD29F5F} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9DD4022C-A010-4A9B-BCC5-171566D4CB17}
Expand Down
263 changes: 263 additions & 0 deletions src/Microsoft.Data.Analysis.IO/DataFrameIO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// Licensed to the .NET Foundation under one or more agreements.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is mostly a duplicate of M.D.A.DataFrameIO.cs. I've called out the minor changes as comments

// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.VisualBasic.FileIO;

namespace Microsoft.Data.Analysis.IO
{
public static class DataFrameIO
{
private static string GetColumnName(string[] columnNames, int columnIndex)
{
return columnNames == null ? "Column" + columnIndex.ToString() : columnNames[columnIndex];
}

private static DataFrameColumn CreateColumn(Type kind, string[] columnNames, int columnIndex)
{
DataFrameColumn ret;
if (kind == typeof(bool))
{
ret = new BooleanDataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(int))
{
ret = new Int32DataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(float))
{
ret = new SingleDataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(string))
{
ret = new StringDataFrameColumn(GetColumnName(columnNames, columnIndex), 0);
}
else if (kind == typeof(long))
{
ret = new Int64DataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(decimal))
{
ret = new DecimalDataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(byte))
{
ret = new ByteDataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(char))
{
ret = new CharDataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(double))
{
ret = new DoubleDataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(sbyte))
{
ret = new SByteDataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(short))
{
ret = new Int16DataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(uint))
{
ret = new UInt32DataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(ulong))
{
ret = new UInt64DataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else if (kind == typeof(ushort))
{
ret = new UInt16DataFrameColumn(GetColumnName(columnNames, columnIndex));
}
else
{
throw new NotSupportedException(nameof(kind));
}
return ret;
}

private static Type MaxKind(Type a, Type b)
{
if (a == typeof(string) || b == typeof(string))
return typeof(string);
if (a == typeof(float) || b == typeof(float))
return typeof(float);
if (a == typeof(bool) || b == typeof(bool))
return typeof(bool);
return typeof(string);
}

private static Type DetermineType(bool first, Type suggested, Type previous)
{
return first ? suggested : MaxKind(suggested, previous);
}

private static Type GuessKind(int col, List<string[]> read)
{
Type res = typeof(string);
int nbline = 0;
foreach (var line in read)
{
if (col >= line.Length)
throw new FormatException(string.Format(Strings.LessColumnsThatExpected, nbline + 1));

string val = line[col];

if (string.Equals(val, "null", StringComparison.OrdinalIgnoreCase))
{
continue;
}

bool boolParse = bool.TryParse(val, out bool boolResult);
if (boolParse)
{
res = DetermineType(nbline == 0, typeof(bool), res);
++nbline;
continue;
}
else
{
if (string.IsNullOrEmpty(val))
{
res = DetermineType(nbline == 0, typeof(bool), res);
continue;
}
}
bool floatParse = float.TryParse(val, out float floatResult);
if (floatParse)
{
res = DetermineType(nbline == 0, typeof(float), res);
++nbline;
continue;
}
else
{
if (string.IsNullOrEmpty(val))
{
res = DetermineType(nbline == 0, typeof(float), res);
continue;
}
}
res = DetermineType(nbline == 0, typeof(string), res);
++nbline;
}
return res;
}

public static DataFrame LoadCsv(Func<Stream> csvStream,

@pgovind pgovind Jun 1, 2020 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LoadCsv method parameters are slightly modified here.

Action<TextFieldParser> SetTextFieldParserProperties,
char separator = ',', bool header = true,
string[] columnNames = null, Type[] dataTypes = null,
long numberOfRowsToRead = -1, int guessRows = 10, bool addIndexColumn = false,
Encoding encoding = null)
{
Stream stream = csvStream();
if (!stream.CanSeek)
{
throw new ArgumentException(Strings.NonSeekableStream, nameof(stream));
}

if (dataTypes == null && guessRows <= 0)
{
throw new ArgumentException(string.Format(Strings.ExpectedEitherGuessRowsOrDataTypes, nameof(guessRows), nameof(dataTypes)));
}

var linesForGuessType = new List<string[]>();
long rowline = 0;
int numberOfColumns = dataTypes?.Length ?? 0;

if (header == true && numberOfRowsToRead != -1)
{
numberOfRowsToRead++;
}

List<DataFrameColumn> columns;
// First pass: schema and number of rows.
using (TextFieldParser parser = new TextFieldParser(stream, defaultEncoding: encoding ?? Encoding.UTF8))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uses TextFieldParser instead of reading a stream. TextFieldParser handles quotes correctly and is in-box

{
parser.SetDelimiters(separator.ToString());
SetTextFieldParserProperties(parser);
while (!parser.EndOfData)
{
if ((numberOfRowsToRead == -1) || rowline < numberOfRowsToRead)
{
if (linesForGuessType.Count < guessRows || (header && rowline == 0))
{
var spl = parser.ReadFields();
if (header && rowline == 0)
{
if (columnNames == null)
{
columnNames = spl;
}
}
else
{
linesForGuessType.Add(spl);
numberOfColumns = Math.Max(numberOfColumns, spl.Length);
}
}
}
++rowline;
if (rowline == guessRows || guessRows == 0)
{
break;
}
}

if (rowline == 0)
{
throw new FormatException(Strings.EmptyFile);
}

columns = new List<DataFrameColumn>(numberOfColumns);
// Guesses types or looks up dataTypes and adds columns.
for (int i = 0; i < numberOfColumns; ++i)
{
Type kind = dataTypes == null ? GuessKind(i, linesForGuessType) : dataTypes[i];
columns.Add(CreateColumn(kind, columnNames, i));
}
}

using (TextFieldParser parser = new TextFieldParser(csvStream(), defaultEncoding: encoding ?? Encoding.UTF8))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextFieldParser seems to be forward-only. So I'm making 2 calls here as a quick proof of concept

{
parser.SetDelimiters(separator.ToString());
SetTextFieldParserProperties(parser);
DataFrame ret = new DataFrame(columns);
// Fills values.
rowline = 0;
while (!parser.EndOfData && (numberOfRowsToRead == -1 || rowline < numberOfRowsToRead))
{
var spl = parser.ReadFields();
if (header && rowline == 0)
{
// Skips.
}
else
{
ret.Append(spl, inPlace: true);
}
++rowline;
}

if (addIndexColumn)
{
PrimitiveDataFrameColumn<int> indexColumn = new PrimitiveDataFrameColumn<int>("IndexColumn", columns[0].Length);
for (int i = 0; i < columns[0].Length; i++)
{
indexColumn[i] = i;
}
columns.Insert(0, indexColumn);
}
return ret;
}
}
}
}
37 changes: 37 additions & 0 deletions src/Microsoft.Data.Analysis.IO/Microsoft.Data.Analysis.IO.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net461</TargetFrameworks>
<SuppressFinalPackageVersion>false</SuppressFinalPackageVersion>
<VersionPrefix>0.1.0</VersionPrefix>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<Description>This package contains IO libraries that work with the Microsoft.Data.Analysis package.</Description>
<PackageReleaseNotes>Initial preview of IO routines that import data into a Microsoft.Data.Analysis.DataFrame.</PackageReleaseNotes>
<PackageTags>Data Science DataFrame Preparation DataView Analytics Exploration</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.Data.Analysis\Microsoft.Data.Analysis.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.VisualBasic" />
</ItemGroup>

<ItemGroup>
<Compile Update="Strings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Strings.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
Loading