Imported Arrow strings lose nulls when exported in nonzero-offset record batches
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- Medio día
- Aptitud para principiantes
- 66/100
Línea de trabajo
The bug is in the null-count loop of ArrowStringDataFrameColumn.GetNullCount in src/Microsoft.Data.Analysis/DataFrameColumns/ArrowStringDataFrameColumn.cs, which uses i < numberOfRows where it should scan from startIndex to startIndex + numberOfRows. Check the same pattern in PrimitiveDataFrameColumn<T>.GetNullCount. Done when a small unit test with a nonzero-offset slice (not the multi-GB repro) shows the correct NullCount and IsNull for the final row.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Description
An Arrow record imported with DataFrame.FromArrowRecordBatch() can export a null string as a valid empty string when ToArrowRecordBatches() divides the DataFrame into multiple batches.
Reproduced on Microsoft.Data.Analysis 0.23.0, Apache.Arrow 14.0.2, Windows x64, .NET 10.
Reproduction
Create a console app, add Microsoft.Data.Analysis version 0.23.0, and run:
using System;
using System.Linq;
using Apache.Arrow;
using Microsoft.Data.Analysis;
const int rows = 268_304_384;
var builder = new StringArray.Builder().Reserve(rows);
builder.Append("a");
builder.AppendRange(Enumerable.Repeat("", rows - 2));
builder.AppendNull();
var input = new RecordBatch.Builder()
.Append("strings", true, builder.Build())
.Build();
var frame = DataFrame.FromArrowRecordBatch(input);
var numbers = new Int64DataFrameColumn("numbers");
numbers.AppendMany(42, rows);
frame.Columns.Add(numbers);
Console.WriteLine($"Imported rows={frame.Rows.Count}; NullCount={frame.Columns["strings"].NullCount}; final string is null={frame[rows - 1, 0] is null}");
int index = 0;
foreach (var batch in frame.ToArrowRecordBatches())
{
var strings = batch.Arrays.OfType<StringArray>().Last();
Console.WriteLine($"Batch {index++}: rows={batch.Length}; last string array length={strings.Length}; NullCount={strings.NullCount}");
if (strings.Length == 1)
Console.WriteLine($"Final exported string={(strings.GetString(0) is { } value ? $"\"{value}\"" : "null")}; IsNull={strings.IsNull(0)}");
}
This deliberately large input needs several GB of RAM. The numeric column naturally occupies two buffers and forces two record batches. The strings remain in one buffer; their final null is exported as a nonzero-offset, one-row slice. A small, single-batch input would not reach the faulty loop bounds.
Actual output
Imported rows=268304384; NullCount=1; final string is null=True
Batch 0: rows=268304383; last string array length=268304383; NullCount=0
Batch 1: rows=1; last string array length=1; NullCount=0
Final exported string=""; IsNull=False
Expected behavior
The final string array should have NullCount == 1, IsNull(0) == true, and GetString(0) == null.
The repro selects the last string array because there is a separate bug where ToArrowRecordBatches() includes previous batches' arrays in later batches (https://github.com/dotnet/machinelearning/issues/7799). This does not cause the newly exported string array's incorrect null metadata.
Cause
ArrowStringDataFrameColumn.GetNullCount loops with i < numberOfRows, treating a row count as an exclusive end index. It should scan [startIndex, startIndex + numberOfRows).
For the final batch, startIndex == 268304383 and numberOfRows == 1, so no validity bits are checked and the null count is incorrectly zero. PrimitiveDataFrameColumn<T>.GetNullCount contains the same loop-bound pattern and should also be considered when fixing this.
- Lenguaje dominante
- C#
- Estrellas
- 9.4k
- Forks
- 2k
- Merge medio
- 2 d 18 h
- PR fusionados (30 d)
- 64
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de dotnet/machinelearning
-
area-Native untriaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
dotnet/machinelearning#7837 ·
Los mantenedores suelen responder en 1 día
-
area-TensorFlow untriaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
dotnet/machinelearning#7836 ·
Los mantenedores suelen responder en 1 día
-
ToArrowRecordBatches accumulates arrays from previous batches, producing invalid record batchesAbiertoarea-DataFrame bug untriaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
dotnet/machinelearning#7799 ·
Los mantenedores suelen responder en 1 día
-
LlamaTokenizer encodes some runs of repeated characters differently from SentencePiecePosiblemente ocupada @Laurianti la tomó hace 42 días. Abiertoarea-Tokenizers untriaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
dotnet/machinelearning#7751 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
BertTokenizer merges words separated only by `\n`, `\t` or `\r`Posiblemente ocupada @Laurianti la tomó hace 23 días. Abiertoarea-Tokenizers green
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
dotnet/machinelearning#7724 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
Todos los issues de dotnet/machinelearning
Issues similares
-
bug emitter:client:csharp
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
microsoft/typespec#12572 · 1 comentario · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
needs-area-label
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 2 días
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
newrelic/newrelic-dotnet-agent#3949 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
area-networking
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
dotnet/aspnetcore#70545 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 87/100
Los mantenedores suelen responder en 1 día