I am getting (gratis) a "Grand Total" column in my PivotTable:
For "TotalQty" and "TotalPrice" this is great, but for "AvgPrice" and "PrcntgOfTotal" not so much.
How can I get these vals to show the average in the first case rather than a sum?
the month percentages are the % of the TotalPrice for the sum of that month (not the overall sum of all months); so GrandTotal.PrcntgOfTotal should not be an average of the monthly values, either.
Here is the code that I have that generates this PivotTable:
private void PopulatePivotTableSheet()
{
string NORTHWEST_CORNER_OF_PIVOT_TABLE = "A6";
AddPrePivotTableDataToPivotTableSheet();
var dataRange = rawDataWorksheet.Cells[rawDataWorksheet.Dimension.Address];
dataRange.AutoFitColumns();
var pivotTable = pivotTableWorksheet.PivotTables.Add(
pivotTableWorksheet.Cells[NORTHWEST_CORNER_OF_PIVOT_TABLE],
dataRange,
"PivotTable");
pivotTable.MultipleFieldFilters = true;
//pivotTable.RowGrandTotals = true; <= default setting
//pivotTable.ColumGrandTotals = true; <= default setting
//pivotTable.RowGrandTotals = false; // this prevents the "Grand Total" column
//pivotTable.ColumGrandTotals = false; // this prevents the totals rows at the bottom
//pivotTable.Compact = true;
//pivotTable.CompactData = true;
pivotTable.GridDropZones = false;
pivotTable.Outline = false;
pivotTable.OutlineData = false;
pivotTable.ShowError = true;
pivotTable.ErrorCaption = "[error]";
pivotTable.ShowHeaders = true;
pivotTable.UseAutoFormatting = true;
pivotTable.ApplyWidthHeightFormats = true;
pivotTable.ShowDrill = true;
// Row field[s]
var descRowField = pivotTable.Fields["Description"];
pivotTable.RowFields.Add(descRowField);
// Column field[s]
var monthYrColField = pivotTable.Fields["MonthYr"];
pivotTable.ColumnFields.Add(monthYrColField);
// Data field[s]
var totQtyField = pivotTable.Fields["TotalQty"];
pivotTable.DataFields.Add(totQtyField);
var totPriceField = pivotTable.Fields["TotalPrice"];
pivotTable.DataFields.Add(totPriceField);
// Don't know how to calc these vals here, so had to put them on the data sheet
var avgPriceField = pivotTable.Fields["AvgPrice"];
pivotTable.DataFields.Add(avgPriceField);
var prcntgOfTotalField = pivotTable.Fields["PrcntgOfTotal"];
pivotTable.DataFields.Add(prcntgOfTotalField);
}
pivotTable.DataFields[DATAFIELD NUM].Function =
OfficeOpenXml.Table.PivotTable.DataFieldFunctions.Average
Note that the DATAFIELD NUM is the index in the data field collection. Defined in:
OfficeOpenXml.Table.PivotTable.ExcelPivotTableDataFieldCollection
Related
I'm trying to filter the last column on a worksheet but I can't seem to get the Index of the column. To be clear, I need the index relative to the worksheet, no the range. I used VisibleView to find the Column, but there may be hidden rows, so my plan is to then load that column via getRangeByIndexes but I need the relative columnIndex to the worksheet.
I've tried a bunch of variations of the below, but I either get Object doesn't support 'getColumn' or columnIndex is undefined
Note: In the below example I've hardcoded 7 as that will be the last column relative to the VisibleView (Columns and rows are already hidden), but I'd like this to by dynamic for other functions and just returnthe "last visible column index".
var ws = context.workbook.worksheets.getActiveWorksheet()
var visible_rng = ws.getUsedRange(true).getVisibleView()
visible_rng.load(["columnCount", "columnIndex"])
await context.sync();
console.log('visible_rng.columnIndex')
console.log(visible_rng.getCell(0,7).columnIndex)
console.log(visible_rng.getColumn(7).columnIndex)
Well this method seems a bit hacky, please share if you know a better way! But, first thing I found was that getVisibleView only metions rows in the Description.
Represents the visible rows of the current range.
I decided to try getSpecialCells and was able to load the address property. I then had to use split and get the last column LETTER and convert this to the Index.
I also wanted the columnCount but this wasn't working w/ getSpecialCells so I polled that from getVisibleView and return an Object relating to Visible Views that I can build on the function later if I need more details.
Here it is:
async function Get_Visible_View_Details_Obj(context, ws) {
var visible_rng = ws.getUsedRange(true).getSpecialCells("Visible");
visible_rng.load("address")
var visible_view_rng = ws.getUsedRange(true).getVisibleView()
visible_view_rng.load("columnCount")
await context.sync();
var Filter_Col_Index = visible_rng.address
var Filter_Col_Index = Filter_Col_Index.split(",")
var Filter_Col_Index = Filter_Col_Index[Filter_Col_Index.length - 1]
var Filter_Col_Index = Filter_Col_Index.split("!")[1]
if (Filter_Col_Index.includes(":") == true) {
var Filter_Col_Index = Filter_Col_Index.split(":")[1]
}
var Filter_Col_Index = Get_Alpha_FromString(Filter_Col_Index)
var Filter_Col_Index = Get_Col_Index_From_Letters(Filter_Col_Index)
var Filter_Col_Index_Obj = {
"last_col_ws_index": Filter_Col_Index,
"columnCount": visible_view_rng.columnCount,
}
return Filter_Col_Index_Obj
}
Helper Funcs:
function Get_Alpha_FromString(str) {
return str.replace(/[^a-z]/gi, '');
}
function Get_Col_Index_From_Letters(str) {
str = str.toUpperCase();
let out = 0, len = str.length;
for (pos = 0; pos < len; pos++) {
out += (str.charCodeAt(pos) - 64) * Math.pow(26, len - pos - 1);
}
return out - 1;
}
Read pptx template then use new data to replace it, there is a scatter chart
associated excel data
xVal and yVal could replace successfully but how to replace C column (extList) ?
xVal and yVal replace by below manner
final CTScatterSer ser = serList.get(0);
final CTAxDataSource xVal = ser.getXVal();
final CTNumDataSource yVal = ser.getYVal();
final CTExtension ctExtension = ser.getExtLst().getExtList().get(0);
final long ptCount = xVal.getNumRef().getNumCache().getPtCount().getVal();
for (int i = 0; i < scData.size(); i++) {
SCNameDouble data = scData.get(i);
CTNumVal xNumVal = ptCount > i ? xVal.getNumRef().getNumCache().getPtArray(i)
: xVal.getNumRef().getNumCache().addNewPt();
xNumVal.setIdx(i);
xNumVal.setV(String.format("%.2f", data.xValue));
CTNumVal yNumVal = ptCount > i ? yVal.getNumRef().getNumCache().getPtArray(i)
: yVal.getNumRef().getNumCache().addNewPt();
yNumVal.setIdx(i);
yNumVal.setV(String.format("%.2f", data.yValue));
}
final int newSize = scData.size();
xVal.getNumRef().setF(
replaceRowEnd(xVal.getNumRef().getF(),
ptCount,
newSize));
yVal.getNumRef().setF(
replaceRowEnd(yVal.getNumRef().getF(),
ptCount,
newSize));
xVal.getNumRef().getNumCache().getPtCount().setVal(newSize);
yVal.getNumRef().getNumCache().getPtCount().setVal(newSize);
Using current apache poi versions one should not trying manipulating charts using the low level CT... classes. There is XDDF for such cases now.
If it comes to PowerPoint charts, then the need is always updating the data in the embedded workbook and updating the data in the chart. See Java edit bar chart in ppt by using poi for an example using bar chart.
Of course a scatter chart is another case then as it not has a category axis but has two value axes. But this also can be updated using XDDF.
The biggest problem you have is the data labels. There is not full support for chart data labels in XDDF upto now. And since you are talkig about extLst and your Excel table shows the data labels in a cell range, I suspect you have set the data labels comming from a cell range. This is a new feature which was not present when Microsoft had published Office Open XML. So not even the low level CT... classes are able to support that feature.
The only way is to manipulate the XML using pure XML manupulating based on org.apache.xmlbeans.XmlObject.
The following shows this on sample of a template you seems to use according to your question.
ScatterChartSample.pptx:
Code:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.AreaReference;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumns;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;
public class PowerPointChangeScatterChartData {
//patched version of XSSFTable.updateHeaders, see https://stackoverflow.com/questions/55532006/renaming-headers-of-xssftable-with-apache-poi-leads-to-corrupt-xlsx-file/55539181#55539181
static void updateHeaders(XSSFTable table) {
XSSFSheet sheet = (XSSFSheet)table.getParent();
CellReference ref = table.getStartCellReference();
if (ref == null) return;
int headerRow = ref.getRow();
int firstHeaderColumn = ref.getCol();
XSSFRow row = sheet.getRow(headerRow);
DataFormatter formatter = new DataFormatter();
if (row != null /*&& row.getCTRow().validate()*/) {
int cellnum = firstHeaderColumn;
CTTableColumns ctTableColumns = table.getCTTable().getTableColumns();
if(ctTableColumns != null) {
for (CTTableColumn col : ctTableColumns.getTableColumnList()) {
XSSFCell cell = row.getCell(cellnum);
if (cell != null) {
col.setName(formatter.formatCellValue(cell));
}
cellnum++;
}
}
}
}
static void updateScatterChart(XSLFChart chart, Object[][] data) throws Exception {
// get chart's data source which is a Excel sheet
XSSFWorkbook chartDataWorkbook = chart.getWorkbook();
String sheetName = chartDataWorkbook.getSheetName(0);
XSSFSheet chartDataSheet = chartDataWorkbook.getSheet(sheetName);
// current Office uses a table as data source
// so get that table if present
XSSFTable chartDataTable = null;
if (chartDataSheet.getTables().size() > 0) {
chartDataTable = chartDataSheet.getTables().get(0);
}
if (chart.getChartSeries().size() == 1) { // we will process only one chart data
XDDFChartData chartData = chart.getChartSeries().get(0);
if (chartData.getSeriesCount() == 1) { // we will process only templates having one series
int rMin = 1; // first row (0) is headers row
int rMax = data.length - 1;
// column 0 is X-Values
int c = 0;
// set new x data
XDDFDataSource xs = null;
for (int r = rMin; r <= rMax; r++) {
XSSFRow row = chartDataSheet.getRow(r); if (row == null) row = chartDataSheet.createRow(r);
XSSFCell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
cell.setCellValue((Double)data[r][c]); // in sheet
}
xs = XDDFDataSourcesFactory.fromNumericCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c)); // in chart
// set new x-title in sheet
String xTitle = (String)data[0][c];
chartDataSheet.getRow(0).getCell(c).setCellValue(xTitle); // in sheet
// column 1 is Y-Values
c = 1;
// set new y data in sheet and in chart
XDDFNumericalDataSource<Double> ys = null;
for (int r = rMin; r <= rMax; r++) {
XSSFRow row = chartDataSheet.getRow(r); if (row == null) row = chartDataSheet.createRow(r);
XSSFCell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
cell.setCellValue((Double)data[r][c]); // in sheet
}
ys = XDDFDataSourcesFactory.fromNumericCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c));
XDDFChartData.Series series1 = chartData.getSeries(0);
series1.replaceData(xs, ys); // in chart
// set new y-title in sheet and in chart
String yTitle = (String)data[0][c];
chartDataSheet.getRow(0).getCell(c).setCellValue(yTitle); // in sheet
series1.setTitle(yTitle, new CellReference(sheetName, 0, c, true, true)); // in chart
series1.plot();
// column 2 is data-labels-range
c = 2;
// set new data labels data in sheet and in chart
XDDFDataSource dataLabelsRangeSource = null;
for (int r = rMin; r <= rMax; r++) {
XSSFRow row = chartDataSheet.getRow(r); if (row == null) row = chartDataSheet.createRow(r);
XSSFCell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
cell.setCellValue((String)data[r][c]); // in sheet
}
dataLabelsRangeSource = XDDFDataSourcesFactory.fromStringCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c)); // in chart
updateDataLabelsRange(chart, dataLabelsRangeSource); // in chart
// set new data-labels-title in sheet
String descrTitle = (String)data[0][c];
chartDataSheet.getRow(0).getCell(c).setCellValue(descrTitle); // in sheet
// update the table if present
if (chartDataTable != null) {
CellReference topLeft = new CellReference(chartDataSheet.getRow(0).getCell(0));
CellReference bottomRight = new CellReference(chartDataSheet.getRow(rMax).getCell(c));
AreaReference tableArea = chartDataWorkbook.getCreationHelper().createAreaReference(topLeft, bottomRight);
chartDataTable.setArea(tableArea);
updateHeaders(chartDataTable);
}
}
}
}
static void updateDataLabelsRange(XDDFChart chart, XDDFDataSource dataLabelsRangeSource) {
String declareNameSpaces = "declare namespace c='http://schemas.openxmlformats.org/drawingml/2006/chart'; "
+ "declare namespace c15='http://schemas.microsoft.com/office/drawing/2012/chart' ";
org.apache.xmlbeans.XmlObject[] selectedObjects = chart.getCTChart().selectPath(
declareNameSpaces
+ ".//c:ext[c15:datalabelsRange]"); // needs net.sf.saxon - Saxon-HE (Saxon-HE-10.6.jar)
if (selectedObjects.length > 0) { // we have at least one ext containing datalabelsRange
org.apache.xmlbeans.XmlObject ext = selectedObjects[0]; // get first ext containing datalabelsRange
// get dataLabelsRange
org.apache.xmlbeans.XmlObject[] datalabelsRanges = ext.selectChildren(new javax.xml.namespace.QName("http://schemas.microsoft.com/office/drawing/2012/chart", "datalabelsRange", "c15"));
org.apache.xmlbeans.XmlObject dataLabelsRange = datalabelsRanges[0];
// set formula
org.apache.xmlbeans.XmlObject[] formulas = dataLabelsRange.selectChildren(new javax.xml.namespace.QName("http://schemas.microsoft.com/office/drawing/2012/chart", "f", "c15"));
org.apache.xmlbeans.XmlObject formula = formulas[0];
((org.apache.xmlbeans.impl.values.XmlObjectBase)formula).setStringValue(dataLabelsRangeSource.getFormula());
// get dlblRangeCache
org.apache.xmlbeans.XmlObject[] dlblRangeCaches = dataLabelsRange.selectChildren(new javax.xml.namespace.QName("http://schemas.microsoft.com/office/drawing/2012/chart", "dlblRangeCache", "c15"));
org.apache.xmlbeans.XmlObject dlblRangeCache = dlblRangeCaches[0];
// empty the cache
dlblRangeCache.newCursor().removeXmlContents();
// create new cache from dataLabelsRangeSource
org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData cache = org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData.Factory.newInstance();
dataLabelsRangeSource.fillStringCache(cache);
// set new cache
dlblRangeCache.set(cache);
}
}
public static void main(String[] args) throws Exception {
String filePath = "ScatterChartSample.pptx"; // has template scatter chart
String filePathNew = "ScatterChartSample_New.pptx";
Object[][] data = new Object[][] { // new data 1 series, 6 x-y-values and data labels
{"X-Values", "Y-Values", "DataLabels"}, // series title
{0.7d, 1.7d, "aa"}, // x1
{1.8d, 3.2d, "bb"}, // x2
{2.6d, 2.8d, "cc"}, // x3
{1.7d, 3.7d, "dd"}, // x4
{2.8d, 4.2d, "ee"}, // x5
{3.6d, 1.8d, "ff"} // x6
};
XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream(filePath));
XSLFChart chart = slideShow.getCharts().get(0);
updateScatterChart(chart, data);
FileOutputStream out = new FileOutputStream(filePathNew);
slideShow.write(out);
out.close();
slideShow.close();
}
}
Resulting ScatterChartSample_New.pptx:
Note: Tested and works using current apache poi 5.2.0.
To be able to use XPath as .//c:ext[c15:datalabelsRange] it needs net.sf.saxon - Saxon-HE (Saxon-HE-10.6.jar in my case).
And it needs poi-ooxml-full-5.2.0.jar and not only the lite version of ooxml-schemas.
How to read Column wise values from excel using nodejs(node-xlsx) ? Please advise.
I want to read Col A data, then Col B and so on, if any data exists in other columns, then put data in array.
I am able to read A1, B1, A2, B2... this way but not A1, A2, A3... then B1, B2, B3... etc.
Sample column wise data
I did custom way but not able to go to col C dynamically.
const xlsxfile = require("xlsx");
var arr = [];
const spreadsheet = xlsxfile.readFile('./Code.xlsx');
const sheets = spreadsheet.SheetNames;
console.log('Sheet Names -- ' + sheets);
const firstSheetName = sheets[0];
const firstSheet = spreadsheet.Sheets[firstSheetName];
console.log(firstSheet);
for (z in firstSheet) {
if (z[0] === '!') continue;
//parse out the column, row, and value
var tt = 0;
for (var i = 0; i < z.length; i++) {
if (!isNaN(z[i])) {
tt = i;
break;
}
};
var col = z.substring(0, tt);
var row = parseInt(z.substring(tt));
for (; ; row++) { // looping over all rows in a column
const firstCol = firstSheet[col+''+row];
if (!firstCol) {
break;
}
let value = firstCol.w;
//console.log(value);
if (value)
if (!arr.includes(value))
arr.push(value);
else
continue;
else
break;
}
}
console.log('final array = '+arr);
I somehow achieved my goal in very bad way. In top most loop it's always trying to get A1, B1, C1 like that. But in between I hijacked and looping all rows.
So in that way, I am keeping one track of what's my last column and comparing in next is same column or new column. if same column, I am existing.
But is there any better way to get all values may be in array format column wise?
const xlsxfile = require("xlsx");
var arr = [];
const spreadsheet = xlsxfile.readFile('./Code.xlsx');
const sheets = spreadsheet.SheetNames;
console.log('Sheet Names -- ' + sheets);
const firstSheetName = sheets[0];
const firstSheet = spreadsheet.Sheets[firstSheetName];
console.log(firstSheet);
let earliercol = []; // keeping a track
for (z in firstSheet) {
if (z[0] === '!') continue;
//parse out the column, row, and value
let tt = 0;
for (let i = 0; i < z.length; i++) {
if (!isNaN(z[i])) {
tt = i;
break;
}
};
let col = z.substring(0, tt);
let row = parseInt(z.substring(tt));
if (earliercol.includes(col)) // checking current col already traversed or not
break;
earliercol.push(col); // assigning current col name
//var value = worksheet[z].v;
for (; ; row++) {
//const firstCol = firstSheet['A' + i];
const firstCol = firstSheet[col+''+row];
if (!firstCol) {
break;
}
let value = firstCol.w;
//console.log(value);
if (value)
if (!arr.includes(value))
arr.push(value);
else
continue;
else
break;
}
}
console.log('final array = '+arr);
I've a view on which I performed a search. I would like to know how many rows this view has after this search. I tried with rows = view1.getEntryCount();
But this gives the number of lines of the "original" view, not the result of my search.
edit
The following works but isn't verry efficient.
Any better idea ?
productTest=sessionScope.get("product");
landTest=sessionScope.get("country");
var length = view1.getEntryCount();
var entries = view1.getAllEntries();
var i = 0;
var rows = 0;
var currentEntry = entries.getFirstEntry();
while(i < length){
land = currentEntry.getColumnValues().elementAt(0);
prod = currentEntry.getColumnValues().elementAt(1);
if (land == landTest & prod == productTest)
{
rows++;
}
currentEntry = entries.getNextEntry();
i++;
}
viewScope.queryString = rows; `
I found this on my blog. Try if if it still works:
var entryCount = viewData.getEntryCount();
var viewControl = getComponent( 'viewControlId' );
var rowCount = viewControl.getRowCount();
// If search is active -> rowcount, else entrycount
var count = ( viewControl.getDataSource().getSearch() ) ? rowCount : entryCount;
please try
rows = view1.getrowlines
I've written a script in google docs to use =importXML function and return the value on its own rather than leaving the function there loading on opening and every hour slowing the thing down.
Basically it uses the data in row D (hidden), sticks the formula in B2, then, overwrites B2 with the value of the formula. I then wanted to repeat this going down the list but just didn't know how - currently I've just repeated the function and changed the cell ID, which I'm aware is a travesty. Could someone guide a noob on how to do it efficiently?
function pullValues()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cellref1 = sheet.getRange("D2");
var ID = cellref1.getValue();
var apistring = "http://api.eve-central.com/api/marketstat?usesystem=30000142&typeid=" + ID;
var command = "importxml(\"" + apistring + "\", \"/evec_api/marketstat/type/sell/min\")";
var cellref2 = sheet.getRange("B2");
cellref2.setFormula(command);
var val = cellref2.getValue();
cellref2.setValue(val);
}
https://docs.google.com/spreadsheet/ccc?key=0AjZlH_sGnj6vdDU4QWdyZTVTd2E4RUFXZnVEZlZJS3c#gid=0
You have to iterate through all rows in the spreadsheet using a for loop. There are many ways to do it, the following code is one of them:
function pullValues()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var startRow = 2; // First row of data to process
var lastRow = sheet.getLastRow(); // Last row of data to process
for (var i = startRow; i <= lastRow; i++)
{
var cellref1 = sheet.getRange("D" + i);
var ID = cellref1.getValue();
var apistring = "http://api.eve-central.com/api/marketstat?usesystem=30000142&typeid=" + ID;
var command = "=ImportXML(\"" + apistring + "\", \"/evec_api/marketstat/type/sell/min\")";
var cellref2 = sheet.getRange("B" + i);
cellref2.setFormula(command);
var val = cellref2.getValue();
cellref2.setValue(val);
}
}