1+ using System ;
2+ using System . Linq ;
3+ using System . Numerics ;
4+ using Microsoft . Data . Analysis ;
5+
6+ namespace Dimension . DataFrame . Extensions ;
7+
8+ /// <summary>
9+ /// Arithmetic extension methods to make Microsoft's DataFrame a little more user-friendly.
10+ /// </summary>
11+ public static class DataFrameExtensionsArithmetic
12+ {
13+ public static PrimitiveDataFrameColumn < T > Plus < T > ( this PrimitiveDataFrameColumn < T > column , PrimitiveDataFrameColumn < T > otherColumn , string name = "" )
14+ where T : unmanaged, INumber < T >
15+ {
16+ return column . Plus < T > ( name , otherColumn ) ;
17+ }
18+
19+ public static PrimitiveDataFrameColumn < T > Plus < T > ( this PrimitiveDataFrameColumn < T > column , string name = "" , params PrimitiveDataFrameColumn < T > [ ] otherColumns )
20+ where T : unmanaged, INumber < T >
21+ {
22+ if ( otherColumns . Any ( c => c . Length != column . Length ) )
23+ {
24+ throw new ArgumentException ( "All columns must have the same length." ) ;
25+ }
26+
27+ var result = new T [ column . Length ] ;
28+ for ( var i = 0 ; i < column . Length ; i ++ )
29+ {
30+ var sum = column [ i ] ?? default ;
31+ foreach ( var otherColumn in otherColumns )
32+ {
33+ sum += otherColumn [ i ] ?? default ;
34+ }
35+
36+ result [ i ] = sum ;
37+ }
38+
39+ if ( string . IsNullOrEmpty ( name ) )
40+ {
41+ var namesToConcat = new [ ] { column . Name } . Concat ( otherColumns . Select ( c => c . Name ) ) ;
42+ name = string . Join ( "+" , namesToConcat ) ;
43+ }
44+
45+ return new PrimitiveDataFrameColumn < T > ( name , result ) ;
46+ }
47+
48+ public static PrimitiveDataFrameColumn < T > Minus < T > ( this PrimitiveDataFrameColumn < T > column , PrimitiveDataFrameColumn < T > columnToSubtract , string name = "" )
49+ where T : unmanaged, INumber < T >
50+ {
51+ if ( column . Length != columnToSubtract . Length )
52+ {
53+ throw new ArgumentException ( "Both columns must have the same length." ) ;
54+ }
55+
56+ var result = new T [ column . Length ] ;
57+ for ( var i = 0 ; i < column . Length ; i ++ )
58+ {
59+ var value1 = column [ i ] ?? default ; // Handle nulls as default(T)
60+ var value2 = columnToSubtract [ i ] ?? default ; // Handle nulls as default(T)
61+ result [ i ] = value1 - value2 ; // Correct use of subtraction with INumber<T>
62+ }
63+
64+ if ( string . IsNullOrEmpty ( name ) )
65+ {
66+ name = $ "{ column . Name } _Minus_{ columnToSubtract . Name } ";
67+ }
68+
69+ return new PrimitiveDataFrameColumn < T > ( name , result ) ;
70+ }
71+
72+ public static PrimitiveDataFrameColumn < T > Times < T > ( this PrimitiveDataFrameColumn < T > column , PrimitiveDataFrameColumn < T > columnToMultiplyBy , string name = "" )
73+ where T : unmanaged, INumber < T >
74+ {
75+ // Wrapper around the Times method with params
76+ return Times ( column , name , columnToMultiplyBy ) ;
77+ }
78+
79+ // Times method for a params array of columns
80+ public static PrimitiveDataFrameColumn < T > Times < T > ( this PrimitiveDataFrameColumn < T > column , string name = "" , params PrimitiveDataFrameColumn < T > [ ] otherColumns )
81+ where T : unmanaged, INumber < T >
82+ {
83+ if ( otherColumns . Any ( c => c . Length != column . Length ) )
84+ {
85+ throw new ArgumentException ( "All columns must have the same length." ) ;
86+ }
87+
88+ var result = new T [ column . Length ] ;
89+ for ( var i = 0 ; i < column . Length ; i ++ )
90+ {
91+ var product = column [ i ] ?? default ;
92+ foreach ( var otherColumn in otherColumns )
93+ {
94+ product *= otherColumn [ i ] ?? default ;
95+ }
96+
97+ result [ i ] = product ;
98+ }
99+
100+ if ( string . IsNullOrEmpty ( name ) )
101+ {
102+ var namesToConcat = new [ ] { column . Name } . Concat ( otherColumns . Select ( c => c . Name ) ) ;
103+ name = $ "{ column . Name } _Times_{ string . Join ( "_" , namesToConcat ) } ";
104+ }
105+
106+ return new PrimitiveDataFrameColumn < T > ( name , result ) ;
107+ }
108+
109+ public static PrimitiveDataFrameColumn < T > Divide < T > ( this PrimitiveDataFrameColumn < T > numeratorColumn , PrimitiveDataFrameColumn < T > divisorColumn , string name )
110+ where T : unmanaged, INumber < T >
111+ {
112+ if ( numeratorColumn . Length != divisorColumn . Length )
113+ {
114+ throw new ArgumentException ( "Both columns must have the same length." ) ;
115+ }
116+
117+ var result = new T [ numeratorColumn . Length ] ;
118+ for ( var i = 0 ; i < numeratorColumn . Length ; i ++ )
119+ {
120+ var numerator = numeratorColumn [ i ] ?? default ; // Handle nulls as default(T)
121+ var divisor = divisorColumn [ i ] ?? default ; // Handle nulls as default(T)
122+
123+ if ( divisor == T . Zero || divisor == default ) // Check for division by zero
124+ {
125+ result [ i ] = typeof ( T ) == typeof ( float ) ? ( T ) ( object ) float . NaN :
126+ typeof ( T ) == typeof ( double ) ? ( T ) ( object ) double . NaN :
127+ default ;
128+ }
129+ else
130+ {
131+ result [ i ] = numerator / divisor ; // Perform division
132+ }
133+ }
134+
135+ if ( string . IsNullOrEmpty ( name ) )
136+ {
137+ name = $ "{ numeratorColumn . Name } /{ divisorColumn . Name } ";
138+ }
139+
140+ return new PrimitiveDataFrameColumn < T > ( name , result ) ;
141+ }
142+ }
0 commit comments