Repository navigation
v0.8.28
Built against Mojo 1.0.0 (ed45d567).
A patch release. A positional column such as #2 and a subscript or slice of text such as s[2:-2] run in SQL. On the pandas side, a comparison, a string question or arithmetic on a row with a missing value now answers what pandas answers, quantile takes the four other interpolation rules and a list, the series reductions take skipna=False and min_count, a category column reduces the way pandas does, DataFrame.from_arrow is there, and the zoned roundings take ambiguous and nonexistent. median and quantile agree with pandas to the last bit, != against a NaN answers True in the vector loops, and the ClickBench driver builds again after a compiler hang that a zone file read had set off. A column now carries an encoding field, which is groundwork for dictionary coded strings, and a pull request runs only the tests its change can reach.
Added
-
A positional column,
#n, which DuckDB reads as the nth column of the FROM counting every table left to right. It works in the select list, WHERE, GROUP BY, HAVING and QUALIFY, and a bareORDER BY #nis the nth output column the wayORDER BY nis. The column keeps its table, so#5over two tables that both have abis the rightb, and the output column is named after it, as in DuckDB.#0, a signed or fractional number and one past 32 bits are the syntax errors DuckDB gives, and one past the last column is DuckDB's out of range error. It is still refused in an ON clause, over a USING or NATURAL join, where DuckDB counts both copies of the merged column, and in a correlated subquery. -
A subscript or a slice of text, such as
s[2],s[-1],s[2:3]ands[2:-2], when the bounds are whole numbers written in the query. DuckDB reads these asarray_extractandarray_slice, and on text both are a substring, so they lower tosubstringwith the numbers worked out while the plan is built. A slice from the front to the back is two cuts. Every shape was checked against DuckDB for strings of up to six characters and every pair of bounds from minus eight to eight. A slice from the back to the front, a bound that is an expression, a step, and a subscript of a list are still refused. -
minandmaxon an ordered category column answer the category at the smallest and largest code, andnuniqueon any category column counts the categories in use, withdropna=Falsecounting a missing row as one more.minanswered the code itself andnuniquerefused the column. Every other reduction on a category column now raises pandas'TypeErrorrather than reducing the codes as numbers:minandmaxwhen the categories are unordered, pointing atas_ordered, andsum,mean,median,std,var,prod,sem,skew,kurt,anyandalloutright.quantileis refused as not supported yet, since pandas reads one off the codes by a rule of its own. (Issue #8) -
quantiletakesinterpolation="lower","higher","midpoint"and"nearest"and a list of quantiles, on a series and down the columns of a frame, all of which were refused. The four rules land on a value in the column or halfway between two, at the position numpy reads, which isqtimes one less than the count with no round trip through a percentage, andnearestrounds a half to the even position asnumpy.arounddoes. The column is sorted once and every position a list needs is read in one take. A list answers a series labelled by the quantiles on a series and a frame with a row per quantile on a frame, and a scalar under one of the four rules answers a series labelled by the columns on a frame.linearis still the kernel, one reduction per quantile. The eight newer rules numpy added are still refused by name, and so is a list or a rule on a groupby and a frame along its rows. The out of range errors now say what pandas 3 says, which is one sentence for a quantile and a list of them alike, and a sentence of its own on a groupby. (Issue #8) -
A string question asked of a missing row answers False, which is what pandas answers. That covers
startswithandendswithwith one prefix or a tuple of them, the tenisquestions about case and kind, andcontains,matchandfullmatchdown both the byte search and the regular expression engine. pandas fills that row with False even withna=Nonewritten out, and withnawhen it is anything else, and the accessor now does both. The kernel still answers null, and the fill happens once in the Python layer for every mask the accessor hands back. A tuple of prefixes also stopped losing track of a missing row when the tuple is empty, sostartswith((), na=True)answers True there, as pandas does. -
A comparison on a row with a missing value answers what pandas answers, which is False for
==,<,<=,>and>=and True for!=, on a series and on a frame, through the operators and the named forms alike. The kernel still answers null there, which is the three valued answer, and the pandas facing operators fill it in, so nothing below them changes. This reverses the choice the compat repository's document 28 recorded and retires the compat divergenceengine/comparison-null, because the library's goal is pandas' answer and a count of the False rows is exactly where a caller would notice the difference. -
skipna=Falseandmin_counton every reduction of a series,nunique(dropna=False)on a series and a frame, andDataFrame.dropna(how="all"), all of which were refused by name. Measured against pandas 3.0.3:skipna=Falseanswers NaN when a value is missing, on an integer column too, and the ordinary answer when none is, andmin_countanswers NaN when fewer values are present than it asks for, including on an integer column and an empty one. Both are applied after the reduction runs, so a column the reduction cannot read still raises the reduction's own error.nunique(dropna=False)counts every missing value as one more distinct value however many there are.dropna(how="all")drops only the rows where every column looked at is missing, with and withoutsubsetandinplace, and raises pandas'ValueErrorfor an unknownhowand itsTypeErrorforhowbesidethresh.skipna=Falseon a temporal column with a gap is still refused, because pandas answersNaTthere and firepanda has noNaTto give back, and the two flags on a whole frame's reductions are still refused as well. -
DataFrame.from_arrow, pandas' door from an Arrow table, which reads it into the types pandas reads it into. An integer column with a missing row comes back as float64 with a NaN in the gap, a float column holds its gap as a NaN and keeps its width, and a column with nothing missing keeps its type, all measured againstpandas.DataFrame.from_arrowin pandas 3.0.3.firepanda.from_arrowis unchanged and still keeps Arrow's types, so there is a door with pandas' name and pandas' meaning and a door with firepanda's own. The widening is thewiden_for_missingthatread_csvalready did, now reachable from Python as_widened_for_missing. (Issue #8) -
tz_localize,floor,ceilandroundtake pandas'ambiguousandnonexistenton a zoned column, on a series and on aDatetimeIndex, where every value but"raise"used to be refused.ambiguoustakes"NaT",True,Falseand a list of flags with one per row, andnonexistenttakes"NaT","shift_forward","shift_backward"and a timedelta. The shifts go by the whole hour of the clock rather than by where the gap ends, because that is what pandas does, so on Lord Howe, where the clock goes forward half an hour, a shift forward lands on the hour after the gap. A timedelta that leaves the reading in the same hour raises pandas' ownValueErrornaming the timedelta. The policy crosses the boundary once as two words and a shift, through a newtemporal_placedmethod besidetemporal_part, and a list of flags is answered by asking twice and taking each row from the side its flag names.ambiguous="infer"is still refused, since it reads the order of the rows rather than being told. (Issue #349)
Added: a column carries its encoding apart from its logical type
AnyArray has an encoding field and an is_flat method, and firepanda.array exports Encoding. Flat is the only encoding there is, so every column says flat and nothing reads the field yet. This is the first box of #979, which is about holding a low cardinality string column as dictionary codes while it still answers dtype string, and later about a filter handing back positions instead of a copy. Both of those are a different layout of the same logical type, so the layout needed a field of its own before either could land.
The field takes the eight bytes _slack used to occupy, and it is eight bytes wide for that reason. Those bytes keep AnyArray from ending short of its alignment, which Mojo miscompiles when the struct is held in an Optional (#286). The struct is the same size it was, and test_a_column_has_no_trailing_padding now counts the encoding where it counted the slack.
Changed: a pull request runs the tests its change can reach, not all 162
The test shards on a pull request now run tools/run_tests.sh --changed, which picks the test files that import something the branch touched and falls back to the whole suite whenever it cannot tell. A push to main still runs everything, so main is gated exactly as before. Over the last eight merged branches this runs nothing for the three that only touched the Python package, 30 files for a planner change, 135 for a kernel change, and the whole suite for the three that touched tools/bindings.py or the manifests. The shards still all start, since the selection is made inside each one, but a shard with nothing to run now finishes green in the time it takes to set up instead of failing.
tools/affected.py no longer treats CHANGELOG.md, docs/, or the Python package as reasons to run the whole suite. No Mojo test reads any of them, which was checked rather than assumed, and without this nearly every branch touched the changelog and selected everything. A .mojo file under python/ still selects the whole suite.
Fixed
-
Arithmetic between two labelled operands answers NaN in a row only one side has, where it answered null, and widens an integer column to float64 to hold it, which is what pandas does. That covers a series with a series and a frame with a frame, through the operators and the named forms, and a frame with a series along either axis. Along the columns pandas lines the series up first, so an integer series missing one of the frame's column labels becomes a float64 series and every column of the answer is a float, including the ones with nothing missing, and that is copied too. The kernel still answers null there and the pandas facing layer turns it into NaN, the same way
shiftcloses the gap it opens. Two operands with the same labels open no gap, so the null an integer division by zero writes is left as it is, which is the registered divergenceengine/zero-divisor. A power is the one operator where pandas can answer 1 in a gap, because numpy's1 ** nanis 1, and that is not copied yet. (Issue #8) -
medianand alinearquantilenow agree with pandas to the last bit, on a series, down the columns of a frame and on a groupby. The value between two neighbours waslow + gap * part, which LLVM fused into one multiply add, so it could land one unit in the last place away from pandas. A median now averages its two middle values the way pandas does, a whole column quantile lands the way numpy's_lerpdoes, from the high side past a half, and a grouped quantile keeps the multiply apart from the add the way pandas' Cython does on x86. The SQL engine keeps its own rule. The rule a reduction lands by rides onAggKindaslanding, which is not part of which reduction it is. (Issue #8) -
!=against a NaN answered False in the vector loops ofkernel/compare.mojo, both between two columns and against a constant, where IEEE, numpy and pandas all answer True.SIMD.nelowers to the ordered compare, which is false on a NaN lane, while the scalar form of the same method is true, so the answer depended on whether the row landed in the vector body or the tail. The loops now take the inverse of equality. -
A program that reads a rule zone and also reaches the readers in
firepanda.iobuilds again. Since #1010 the zone file was read with the standard library'sopen, and the Mojo 1.0.0 compiler stops partway through building such a program, every thread parked and nothing emitted, which is what the ClickBench driver in firepanda-bench had been doing since #1010 merged. The file is now read withfopenandfread, and the driver builds. Nothing about what a zone answers changed.