diff --git a/NEWS.md b/NEWS.md index ead8651853..7e0c341a90 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,7 +38,9 @@ * add `rownumber` to `DataFrameRow` ([#2356](https://github.com/JuliaData/DataFrames.jl/pull/2356)) * allow passing column name to specify the position where a new columns should be inserted in `insertcols!` ([#2365](https://github.com/JuliaData/DataFrames.jl/pull/2365)) - +* add `isapprox` method to check for approximate equality between two dataframes + ([#2373](https://github.com/JuliaData/DataFrames.jl/pull/2373)) + ## Deprecated * `DataFrame!` is now deprecated ([#2338](https://github.com/JuliaData/DataFrames.jl/pull/2338)) diff --git a/Project.toml b/Project.toml index c9534d9fc6..d3bee6ff1e 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,7 @@ DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" Future = "9fa8497b-333b-5362-9e8d-4d0656e87820" InvertedIndices = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" IteratorInterfaceExtensions = "82899510-4779-5014-852e-03e436cf321d" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" PooledArrays = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" diff --git a/docs/src/lib/functions.md b/docs/src/lib/functions.md index 0356492da8..8c62e7ff73 100644 --- a/docs/src/lib/functions.md +++ b/docs/src/lib/functions.md @@ -124,3 +124,8 @@ eachrow values pairs ``` + +## Equality +```@docs +isapprox +``` diff --git a/src/DataFrames.jl b/src/DataFrames.jl index 709deb943e..a8e5bf9b60 100644 --- a/src/DataFrames.jl +++ b/src/DataFrames.jl @@ -5,6 +5,7 @@ using Reexport, SortingAlgorithms, Compat, Unicode, PooledArrays @reexport using CategoricalArrays, Missings, InvertedIndices using Base.Sort, Base.Order, Base.Iterators using TableTraits, IteratorInterfaceExtensions +import LinearAlgebra: norm import DataAPI, DataAPI.All, diff --git a/src/abstractdataframe/abstractdataframe.jl b/src/abstractdataframe/abstractdataframe.jl index 5653c4ceba..66d47ac317 100644 --- a/src/abstractdataframe/abstractdataframe.jl +++ b/src/abstractdataframe/abstractdataframe.jl @@ -410,6 +410,22 @@ function Base.isequal(df1::AbstractDataFrame, df2::AbstractDataFrame) return true end +""" + isapprox(df1::AbstractDataFrame, df2::AbstractDataFrame; + rtol::Real=atol>0 ? 0 : √eps, atol::Real=0, + nans::Bool=false, norm::Function=norm) + +Inexact equality comparison. `df1` and `df2` must have the same size and column names. +Return `true` if `isapprox` with given keyword arguments +applied to all pairs of columns stored in `df1` and `df2` returns `true`. +""" +function Base.isapprox(df1::AbstractDataFrame, df2::AbstractDataFrame; + atol::Real=0, rtol::Real=atol>0 ? 0 : √eps(), + nans::Bool=false, norm::Function=norm) + size(df1) == size(df2) || throw(DimensionMismatch("dimensions must match: a has dims $(size(df1)), b has dims $(size(df2))")) + isequal(index(df1), index(df2)) || throw(ArgumentError("column names of passed data frames do not match")) + return all(isapprox.(eachcol(df1), eachcol(df2), atol=atol, rtol=rtol, nans=nans, norm=norm)) +end ############################################################################## ## ## Description diff --git a/test/constructors.jl b/test/constructors.jl index 6bd54d74a5..38d5e08dda 100644 --- a/test/constructors.jl +++ b/test/constructors.jl @@ -223,6 +223,8 @@ end @test size(df, 1) == 3 @test size(df, 2) == 2 @test isequal(df, DataFrame(x1 = [0.0, 0.0, 0.0], x2 = [1.0, 1.0, 1.0])) + @test isapprox(df, DataFrame(x1 = [0.0, 0.0, 0.0], x2 = [1.0, 1.0, 1.0])) + @test isapprox(df, DataFrame(x1 = [0.0, 0.0, 0.0], x2 = [1.000000010000, 1.0, 1.0])) df = DataFrame(:type => [], :begin => []) @test propertynames(df) == [:type, :begin]