Method: Worksheet#write_formula
- Defined in:
- lib/writeexcel/worksheet.rb
#write_formula(*args) ⇒ Object
:call-seq:
write_formula(row, col , formula[, format, value]) -> Fixnum
write_formula(A1_notation, formula[, format, value]) -> Fixnum
Write a formula to the specified row and column (zero indexed).
format is optional. value is an optional result of the formula that can be supplied by the user.
Returns 0 : normal termination
-1 : insufficient number of arguments
-2 : row or column out of range
Write a formula or function to the cell specified by row and column:
worksheet.write_formula(0, 0, '=$B$3 + B4' )
worksheet.write_formula(1, 0, '=SIN(PI()/4)')
worksheet.write_formula(2, 0, '=SUM(B1:B5)' )
worksheet.write_formula('A4', '=IF(A3>1,"Yes", "No")' )
worksheet.write_formula('A5', '=AVERAGE(1, 2, 3, 4)' )
worksheet.write_formula('A6', '=DATEVALUE("1-Jan-2001")')
See the note about “Cell notation”. For more information about writing Excel formulas see “FORMULAS AND FUNCTIONS IN EXCEL”
See also the section “Improving performance when working with formulas” and the store_formula() and repeat_formula() methods.
If required, it is also possible to specify the calculated value of the formula. This is occasionally necessary when working with non-Excel applications that don’t calculated the value of the formula. The calculated value is added at the end of the argument list:
worksheet.write('A1', '=2+2', format, 4);
However, this probably isn’t something that will ever need to do. If you do use this feature then do so with care.
FORMULAS AND FUNCTIONS IN EXCEL
Caveats
The first thing to note is that there are still some outstanding issues with the implementation of formulas and functions:
1. Writing a formula is much slower than writing the equivalent string.
2. You cannot use array constants, i.e. {1;2;3}, in functions.
3. Unary minus isn't supported.
4. Whitespace is not preserved around operators.
5. Named ranges are not supported.
6. Array formulas are not supported.
However, these constraints will be removed in future versions. They are here because of a trade-off between features and time. Also, it is possible to work around issue 1 using the store_formula() and repeat_formula() methods as described later in this section.
Introduction
The following is a brief introduction to formulas and functions in Excel and WriteExcel.
A formula is a string that begins with an equals sign:
'=A1+B1'
'=AVERAGE(1, 2, 3)'
The formula can contain numbers, strings, boolean values, cell references, cell ranges and functions. Named ranges are not supported. Formulas should be written as they appear in Excel, that is cells and functions must be in uppercase.
Cells in Excel are referenced using the A1 notation system where the column is designated by a letter and the row by a number. Columns range from A to IV i.e. 0 to 255, rows range from 1 to 65536.
The Excel $ notation in cell references is also supported. This allows you to specify whether a row or column is relative or absolute. This only has an effect if the cell is copied. The following examples show relative and absolute values.
'=A1' # Column and row are relative
'=$A1' # Column is absolute and row is relative
'=A$1' # Column is relative and row is absolute
'=$A$1' # Column and row are absolute
Formulas can also refer to cells in other worksheets of the current workbook. For example:
'=Sheet2!A1'
'=Sheet2!A1:A5'
'=Sheet2:Sheet3!A1'
'=Sheet2:Sheet3!A1:A5'
%q{='Test Data'!A1}
%q{='Test Data1:Test Data2'!A1}
The sheet reference and the cell reference are separated by ! the exclamation mark symbol. If worksheet names contain spaces, commas o parentheses then Excel requires that the name is enclosed in single quotes as shown in the last two examples above. In order to avoid using a lot of escape characters you can use the quote operator %q{} to protect the quotes. See perlop in the main Perl documentation. Only valid sheet names that have been added using the add_worksheet() method can be used in formulas. You cannot reference external workbooks.
The following table lists the operators that are available in Excel’s formulas. The majority of the operators are the same as Perl’s, differences are indicated:
Arithmetic operators:
=====================
Operator Meaning Example
+ Addition 1+2
- Subtraction 2-1
* Multiplication 2*3
/ Division 1/4
^ Exponentiation 2^3 # Equivalent to **
- Unary minus -(1+2) # Not yet supported
% Percent (Not modulus) 13% # Not supported, [1]
Comparison operators:
=====================
Operator Meaning Example
= Equal to A1 = B1 # Equivalent to ==
<> Not equal to A1 <> B1 # Equivalent to !=
> Greater than A1 > B1
< Less than A1 < B1
>= Greater than or equal to A1 >= B1
<= Less than or equal to A1 <= B1
String operator:
================
Operator Meaning Example
& Concatenation "Hello " & "World!" # [2]
Reference operators:
====================
Operator Meaning Example
: Range operator A1:A4 # [3]
, Union operator SUM(1, 2+2, B3) # [4]
Notes:
[1]: You can get a percentage with formatting and modulus with MOD().
[2]: Equivalent to ("Hello " . "World!") in Perl.
[3]: This range is equivalent to cells A1, A2, A3 and A4.
[4]: The comma behaves like the list separator in Perl.
The range and comma operators can have different symbols in non-English versions of Excel. These will be supported in a later version of WriteExcel. European users of Excel take note:
worksheet.write('A1', '=SUM(1; 2; 3)') # Wrong!!
worksheet.write('A1', '=SUM(1, 2, 3)') # Okay
The following table lists all of the core functions supported by Excel 5 and WriteExcel. Any additional functions that are available through the “Analysis ToolPak” or other add-ins are not supported. These functions have all been tested to verify that they work.
ABS DB INDIRECT NORMINV SLN
ACOS DCOUNT INFO NORMSDIST SLOPE
ACOSH DCOUNTA INT NORMSINV SMALL
ADDRESS DDB INTERCEPT NOT SQRT
AND DEGREES IPMT NOW STANDARDIZE
AREAS DEVSQ IRR NPER STDEV
ASIN DGET ISBLANK NPV STDEVP
ASINH DMAX ISERR ODD STEYX
ATAN DMIN ISERROR OFFSET SUBSTITUTE
ATAN2 DOLLAR ISLOGICAL OR SUBTOTAL
ATANH DPRODUCT ISNA PEARSON SUM
AVEDEV DSTDEV ISNONTEXT PERCENTILE SUMIF
AVERAGE DSTDEVP ISNUMBER PERCENTRANK SUMPRODUCT
BETADIST DSUM ISREF PERMUT SUMSQ
BETAINV DVAR ISTEXT PI SUMX2MY2
BINOMDIST DVARP KURT PMT SUMX2PY2
CALL ERROR.TYPE LARGE POISSON SUMXMY2
CEILING EVEN LEFT POWER SYD
CELL EXACT LEN PPMT T
CHAR EXP LINEST PROB TAN
CHIDIST EXPONDIST LN PRODUCT TANH
CHIINV FACT LOG PROPER TDIST
CHITEST FALSE LOG10 PV TEXT
CHOOSE FDIST LOGEST QUARTILE TIME
CLEAN FIND LOGINV RADIANS TIMEVALUE
CODE FINV LOGNORMDIST RAND TINV
COLUMN FISHER LOOKUP RANK TODAY
COLUMNS FISHERINV LOWER RATE TRANSPOSE
COMBIN FIXED MATCH REGISTER.ID TREND
CONCATENATE FLOOR MAX REPLACE TRIM
CONFIDENCE FORECAST MDETERM REPT TRIMMEAN
CORREL FREQUENCY MEDIAN RIGHT TRUE
COS FTEST MID ROMAN TRUNC
COSH FV MIN ROUND TTEST
COUNT GAMMADIST MINUTE ROUNDDOWN TYPE
COUNTA GAMMAINV MINVERSE ROUNDUP UPPER
COUNTBLANK GAMMALN MIRR ROW VALUE
COUNTIF GEOMEAN MMULT ROWS VAR
COVAR GROWTH MOD RSQ VARP
CRITBINOM HARMEAN MODE SEARCH VDB
DATE HLOOKUP MONTH SECOND VLOOKUP
DATEVALUE HOUR N SIGN WEEKDAY
DAVERAGE HYPGEOMDIST NA SIN WEIBULL
DAY IF NEGBINOMDIST SINH YEAR
DAYS360 INDEX NORMDIST SKEW ZTEST
– You can also modify the module to support function names in the following languages: German, French, Spanish, Portuguese, Dutch, Finnish, Italian and Swedish. See the function_locale.pl program in the examples directory of the distro. ++
For a general introduction to Excel’s formulas and an explanation of the syntax of the function refer to the Excel help files or the following: office.microsoft.com/en-us/assistance/CH062528031033.aspx
If your formula doesn’t work in Spreadsheet::WriteExcel try the following:
1. Verify that the formula works in Excel (or Gnumeric or OpenOffice.org).
2. Ensure that it isn't on the Caveats list shown above.
3. Ensure that cell references and formula names are in uppercase.
4. Ensure that you are using ':' as the range operator, A1:A4.
5. Ensure that you are using ',' as the union operator, SUM(1,2,3).
6. Ensure that the function is in the above table.
If you go through steps 1-6 and you still have a problem, mail me.
Improving performance when working with formulas
Writing a large number of formulas with Spreadsheet::WriteExcel can be slow. This is due to the fact that each formula has to be parsed and with the current implementation this is computationally expensive.
However, in a lot of cases the formulas that you write will be quite similar, for example:
worksheet.write_formula('B1', '=A1 * 3 + 50', format)
worksheet.write_formula('B2', '=A2 * 3 + 50', format)
...
...
worksheet.write_formula('B99', '=A999 * 3 + 50', format)
worksheet.write_formula('B1000', '=A1000 * 3 + 50', format)
In this example the cell reference changes in iterations from A1 to A1000. The parser treats this variable as a token and arranges it according to predefined rules. However, since the parser is oblivious to the value of the token, it is essentially performing the same calculation 1000 times. This is inefficient.
The way to avoid this inefficiency and thereby speed up the writing of formulas is to parse the formula once and then repeatedly substitute similar tokens.
A formula can be parsed and stored via the store_formula() worksheet method. You can then use the repeat_formula() method to substitute pattern, replace pairs in the stored formula:
formula = worksheet.store_formula('=A1 * 3 + 50')
(0...1000).each do |row|
worksheet.repeat_formula(row, 1, formula, format, 'A1', 'A'.(row +1))
end
On an arbitrary test machine this method was 10 times faster than the brute force method shown above. – For more information about how WriteExcel parses and stores formulas see the WriteExcel::Formula man page.
It should be noted however that the overall speed of direct formula parsing will be improved in a future version. ++
3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 |
# File 'lib/writeexcel/worksheet.rb', line 3085 def write_formula(*args) # Check for a cell reference in A1 notation and substitute row and column if (args[0] =~ /^\D/) args = substitute_cellref(args) end return -1 if args.size < 3 # Check the number of args record = 0x0006 # Record identifier # length # Bytes to follow row = args[0] # Zero indexed row col = args[1] # Zero indexed column formula = args[2].dup # The formula text string value = args[4] # The formula text string xf = xf_record_index(row, col, args[3]) # The cell format chn = 0x0000 # Must be zero is_string = 0 # Formula evaluates to str # num # Current value of formula # grbi # Option flags # Excel normally stores the last calculated value of the formula in num. # Clearly we are not in a position to calculate this "a priori". Instead # we set num to zero and set the option flags in grbit to ensure # automatic calculation of the formula when the file is opened. # As a workaround for some non-Excel apps we also allow the user to # specify the result of the formula. # num, grbit, is_string = encode_formula_result(value) # Check that row and col are valid and store max and min values return -2 if check_dimensions(row, col) != 0 # Strip the = sign at the beginning of the formula string formula.sub!(/^=/, '') # Parse the formula using the parser in Formula.pm # nakamura add: to get byte_stream, set second arg TRUE # because ruby doesn't have Perl's "wantarray" formula = @parser.parse_formula(formula, true) # if ($@) { # $@ =~ s/\n$// # Strip the \n used in the Formula.pm die() # croak $@ # Re-raise the error # } formlen = formula.length # Length of the binary string length = 0x16 + formlen # Length of the record data header = [record, length].pack("vv") data = [row, col, xf].pack("vvv") + num + [grbit, chn, formlen].pack('vVv') # The STRING record if the formula evaluates to a string. string = '' string = get_formula_string(value) if is_string != 0 # Store the data or write immediately depending on the compatibility mode. if @compatibility != 0 tmp = [] tmp[col] = header + data + formula + string @table[row] = tmp else print "sheet #{@name} : #{__FILE__}(#{__LINE__}) \n" if defined?($debug) append(header, data, formula, string) end return 0 end |