I'm trying to export data to excel file (working quite fine) and create a chart.
What I have: 3 columns - ID, value, date. There are multiple rows with same id, but different value and datetime.
Example:
ID - Value - Datetime
1 - 14 - 21.11.2017 2:17:08
1 - 15 - 22.11.2017 14:25:45
3 - 12.5 - 21.11.2017 15:12:12
3 - 18.7 - 21.11.2017 19:27:35
3 - 13 - 22.11.2017 0:47:17
What I want is a chart, where Value is Y axis, each ID is a series, and Datetime is X axis. However, the datetime is different for each ID.
This is how it should look, but with dates instead of numbers on the X axis.
Chart Image.
I've been searching for a solution, and trying solutions from similar questions, but none had been "on point" yet.
This is what I have so far:
Excel.ChartObjects ChartObjects = (Excel.ChartObjects)WS.ChartObjects();
Excel.ChartObject chartObject = ChartObjects.Add(400, 40, 450, 300);
chartObject.Chart.ChartType = Excel.XlChartType.xlXYScatterLines;
Excel.SeriesCollection oSeriesCollection = (Excel.SeriesCollection)chartObject.Chart.SeriesCollection();
Excel.Axis xAxis = (Excel.Axis)chartObject.Chart.Axes(Excel.XlAxisType.xlCategory, Excel.XlAxisGroup.xlPrimary);
xAxis.HasTitle = true;
xAxis.AxisTitle.Text = "Date";
Excel.Axis yAxis = (Excel.Axis)chartObject.Chart.Axes(Excel.XlAxisType.xlValue, Excel.XlAxisGroup.xlPrimary);
yAxis.HasTitle = true;
yAxis.AxisTitle.Text = "Value";
int startPos = 4;
int endPos = 0;
int previousCount = 0;
for (int i = 0; i < Count; i++)
{
startPos = startPos + previousCount;
endPos = startPos + CountList[i].Count - 1;
Excel.Series oSeries = oSeriesCollection.NewSeries();
oSeries.Values = WS.Range["E" + startPos, "E" + endPos];
//oSeries.XValues = WS.Range["F" + startPos, "F" + endPos]; //doesn't really do anything
oSeries.Name = "id " + CountList[i].Number.ToString();
previousCount = CountList[i].Count;
}
If it makes any difference, date is datetime in database, but is converted to string when storing it in List.
How can I set the x axis the way I need it to be? Is it possible?
I did something very similar using EPPlus. It was in asp.net MVC 5, so saving the document might need to be altered to suit your needs. In my case reading age is the value you want to display on Y axis and they have dates on X axis. Hopefully this will at least you give you something to build on. Notice the comment within, otherwise the chart did not work as intended for me.
var results = GetResultsSomehow();
ExcelPackage excel = new ExcelPackage();
var workSheet = excel.Workbook.Worksheets.Add("Reading Ages");
workSheet.TabColor = System.Drawing.Color.Black;
var scatterChart = (ExcelScatterChart)workSheet.Drawings.AddChart("Reading Ages", eChartType.XYScatterSmooth);
scatterChart.SetPosition(3, 1, 4, 1);
scatterChart.SetSize(800, 500);
scatterChart.XAxis.Format = "dd/mm/yyyy";
// must display hidden data and empty as gaps, otherwise the date axis will explode
scatterChart.ShowHiddenData = true;
scatterChart.DisplayBlanksAs = eDisplayBlanksAs.Gap;
int rowIndex = 4;
int stopIndex = 4;
foreach (var student in results.Select(a=> a.Student).Distinct())
{
int start = rowIndex;
int stop = start + stopIndex;
workSheet.Cells[rowIndex, 1].Value = student.Surname;
foreach(var row in results.Where(a=> a.Student == student))
{
workSheet.Cells[rowIndex, 2].Value = row.Age;
workSheet.Cells[rowIndex, 3].Value = row.Date;
rowIndex++;
stopIndex++;
}
stop = rowIndex;
var series = scatterChart.Series.Add(workSheet.Cells[start, 2, stop - 1, 2], workSheet.Cells[start, 3, stop - 1, 3]);
series.Header = student.Surname;
}
string excelName = identifier + " reading ages";
using (var memoryStream = new MemoryStream())
{
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=" + excelName + ".xlsx");
excel.SaveAs(memoryStream);
memoryStream.WriteTo(Response.OutputStream);
Response.Flush();
Response.End();
}
Related
I am trying to find a cause of trouble in my code. FDCTHorizontal transforms all numbers inside 2D array and IDCTHorizontal should transform them back.
Methods work ONLY if one methematical operation is done on one, same element inside of each method. If two operations are made, returned 2D array from IDCTHorizontal is not the same that entered FDCTHorizontal.
Argument differences represenets a row in 2D array. Argument Y and X represent output row values.
Why can't I use more then one mathematical operations in both methods?
Code:
public static void FDCTHorizontal(ref int[] differences, ref int[] Y)
{
for (int j = 0; j < differences.Length; j += 8) //split by 8
{
double[] X = new double[8];
int indexCounter = 0;
for (int _ = j; _ < j + 8; _++)
{
X[indexCounter] = differences[_];
indexCounter++;
}
//1. stage
double X0value = X[0];
double X1value = X[1];
double X2value = X[2];
double X3value = X[3];
double X4value = X[4];
double X5value = X[5];
double X6value = X[6];
double X7value = X[7];
//X[0] = X[0] + X7value; //if not commented, value when calling inverse method will not be the same
X[7] = -X[7] + X0value;
Y[j + 0] = Convert.ToInt32(X[0]);
Y[j + 4] = Convert.ToInt32(X[1]);
Y[j + 2] = Convert.ToInt32(X[2]);
Y[j + 6] = Convert.ToInt32(X[3]);
Y[j + 7] = Convert.ToInt32(X[4]);
Y[j + 3] = Convert.ToInt32(X[5]);
Y[j + 5] = Convert.ToInt32(X[6]);
Y[j + 1] = Convert.ToInt32(X[7]); //switched order
}
}
public static void IDCTHorizontal(ref int[] changedDifferences, ref int[] X)
{
for (int j = 0; j < changedDifferences.Length; j += 8)
{
double[] Y = new double[8];
int indexCounter = 0;
for (int _ = j; _ < j + 8; _++)
{
Y[indexCounter] = changedDifferences[_];
Y[indexCounter] = Y[indexCounter];
indexCounter++;
}
double Y0value = Y[0];
double Y1value = Y[1];
double Y2value = Y[2];
double Y3value = Y[3];
double Y4value = Y[4];
double Y5value = Y[5];
double Y6value = Y[6];
double Y7value = Y[7];
//Y[0] = Y[0] - Y1value; //if not a comment, returned array does not have the right values
Y[1] = -Y[1] + Y0value;
X[j + 0] = Convert.ToInt32(Y[0]);
X[j + 1] = Convert.ToInt32(Y[4]);
X[j + 2] = Convert.ToInt32(Y[2]);
X[j + 3] = Convert.ToInt32(Y[6]);
X[j + 4] = Convert.ToInt32(Y[7]);
X[j + 5] = Convert.ToInt32(Y[3]);
X[j + 6] = Convert.ToInt32(Y[5]);
X[j + 7] = Convert.ToInt32(Y[1]);
}
}
Independently of the correctness of your algorithms, you have a bigger issue: you are performing a banker's rounding in both tranformations. You can't expect both operations to be reversible, because rounding, in general, isn't:
Suppose the following transformation: f(x) = x + 1.5 and the opposite g(x) = x - 1.5. Applying both transformations is obviously a noop: g(f(x)) = x + 1.5 - 1.5 = x.
However, if we add rounding to it, the behavior changes:
f´(x) = BankersRound(x + 1.5)
g´(x) = BankersRound(x - 1.5)
x = 3
f´(3) = BankersRound(4.5) = 4 //round to the nearest even number
g´(f(3)) = BankersRound(4 - 1.5) = BankersRound(2.5) = 2 //round to the nearest even number
UPDATE: Ok, this is not the issue because, as RufusL correctly points out, you are only performing additions and subtractions so rounding shouldn't be an issue in this particual case. But this obviously begs the question: why are you using a double array to begin with? If you are adding and subtracting integers, then use an array of ints! (or longs if you are expecting potenitial overflows in intermediate operations).
Points on my second chart don't fit y-axis as you can see here:
Points values are exactly 50.0000, 49.9999, 49.9998 and 50.0001. But they are not on lines. And when I add point and with it increase number of values on y-axis, then points would fit y-axis, like in this picture.
Here is my code (sorry for Serbian text values)
TacnostVage tacnost = bazaPodataka.UcitajTacnostVage(Convert.ToString(dataGridView2.SelectedRows[0].Cells[2].Value), Convert.ToInt32(comboBox18.Text));
List<TestTacnostVage> testoviTacnost = bazaPodataka.UcitajTestoveTacnostVage(Convert.ToString(dataGridView2.SelectedRows[0].Cells[2].Value), Convert.ToInt32(comboBox18.Text));
chart2.ChartAreas.Clear();
chart2.Series.Clear();
prikažiToolStripMenuItem.DropDownItems.Clear();
tabeluToolStripMenuItem.DropDownItems.Clear();
string format = Convert.ToString(vaga.Podeljak);
format = format.Remove(format.Length - 1, 1) + "0";
if (testoviTacnost.Count != 0)
{
for (int i = 0; i < tacnost.NominalneMase.Count(); i++)
{
ChartArea area = new ChartArea();
Series series = new Series();
area.AxisY.MajorGrid.LineColor = Color.LightGray;
area.AxisX.MajorGrid.LineColor = Color.LightGray;
area.AxisY.LabelStyle.Format = format;
area.BorderColor = Color.LightGray;
area.BorderDashStyle = ChartDashStyle.Solid;
area.AxisY.Interval = vaga.Podeljak;
area.Name = "ChartArea" + (i + 1);
series.ChartType = SeriesChartType.Point;
series.ChartArea = "ChartArea" + (i + 1);
series.Name = "Tačka" + (i + 1);
string text = "";
TegoviTacnostVaga tegoviTacnost = bazaPodataka.UcitajTegoveTacnostVage(Convert.ToString(dataGridView2.SelectedRows[0].Cells[2].Value), Convert.ToInt32(comboBox18.Text), i);
if (tegoviTacnost != null)
{
for (int j = 0; j < tegoviTacnost.Proizvodjac.Count(); j++)
{
text += tegoviTacnost.Proizvodjac[j] + " ";
text += tegoviTacnost.SerijskiBrojevi[j] + " ";
text += tegoviTacnost.NominalneMase[j] + "g";
text += (j == tegoviTacnost.Proizvodjac.Count() - 1 ? "" : "\n");
}
}
series.LegendText = (text == "" ? "Nema podataka" : text);
for (int j = 0; j < testoviTacnost.Count(); j++)
series.Points.AddXY(testoviTacnost[j].RedniBrojTesta, testoviTacnost[j].RezultatiMerenja[i]);
area.AxisY.StripLines.Add(new StripLine() { BorderColor = Color.Red, IntervalOffset = (tacnost.RezultatiMerenja[i].Average() + koeficijentTacnost * ponovljivost.ReferentnaVrednost), Text = "Gornja granica: " + Convert.ToDouble(tacnost.RezultatiMerenja[i].Average() + koeficijentTacnost * ponovljivost.ReferentnaVrednost).ToString(format) });
area.AxisY.StripLines.Add(new StripLine() { BorderColor = Color.Red, IntervalOffset = (tacnost.RezultatiMerenja[i].Average() - koeficijentTacnost * ponovljivost.ReferentnaVrednost), Text = "Donja granica: " + Convert.ToDouble(tacnost.RezultatiMerenja[i].Average() - koeficijentTacnost * ponovljivost.ReferentnaVrednost).ToString(format) });
area.AxisY.Maximum = area.AxisY.StripLines[0].IntervalOffset + area.AxisY.Interval;
if (series.Points.FindMaxByValue().YValues[0] >= area.AxisY.Maximum)
area.AxisY.Maximum = series.Points.FindMaxByValue().YValues[0] + area.AxisY.Interval;
area.AxisY.Minimum = area.AxisY.StripLines[1].IntervalOffset - area.AxisY.Interval;
if (series.Points.FindMinByValue().YValues[0] <= area.AxisY.Minimum)
area.AxisY.Minimum = series.Points.FindMinByValue().YValues[0] - area.AxisY.Interval;
chart2.ChartAreas.Add(area);
chart2.Series.Add(series);
}
}
I found solution, but I'm not sure if this explanation is true. The problem was Axis-Y maximum. Charts Axis-Y interval was 0.0001 (4 decimals), but in my code, I put maximum to be StripLines IntervalOffset (which was more than 4 decimals) plus Charts Interval (in result that is more than 4 decimals). So probably this happens when your Chars Axis-Y Maximum and your Interval (if you set Interval) have different number of decimals. So I just simply rounded Strip Lines InvervalOffset to 4 decimals (in this case), and put Axis-Y Maximum to have 4 decimals like Interval has.
I have a list of objects and I am trying to dedicate three rows for every object and I am using the "Office Open Xml library" with this routine:
int row = 1, col = 1;
for (int i = 0; i < MyList.Count * 3; i+=3)
{
MySheet.Cells[row + i, col, row + i + 1, col].Merge = true; // merge two and get a "result"
MySheet.Cells[row + i + 1, col, row + i + 2, col].Merge = true; // merge "result" with third row
}
However it pops an error saying can't merge already merged cell.
So the question is How to merge more than two cells in Excel?
The indexes specify a range of cells, not only two adjacent cells. To merge cells across three rows you can write
MySheet.Cells[row + i, col, row + i + 2, col].Merge = true;
Or you can merge a 3x2 block
MySheet.Cells[row + i, col, row + i + 2, col+1].Merge = true;
Everything in Excel is a range. Even a single cell is treated as a single-cell. This makes working with single cells a bit weird but it makes applying styles and formulas a lot easier
Use this for merge 2x3
MySheet.get_Range(MySheet.Cells[row, col], MySheet.Cells[row + 2, col + 3]).MergeCells = true;
I am making a
Waterfall chart in a PowerPoint add-in using VSTO.
PowerPoint.Slide slide = null;
PowerPoint.Shape shape = null;
PowerPoint.Chart chart = null;
This lines tells what kind of chart to make.
shape = slide.Shapes.AddChart(Office.XlChartType.xlColumnStacked, 200, 200, 300, 200);
This line here opens an Excel workbook which makes the chart when data is added into it.
chart = shape.Chart;
Here is an image of the chart that I create.
Now the problem is, I want the 2nd bar to be plotted form the end of the 1st bar i.e from 4 instead of 0 on the x-axis.
Like this.
Can someone tell me is there any way I can draw my series from the value of the previous series instead of 0 value of x-axis?
I don't think Excel or PowerPoint charts supports waterfalls out of the box. However, you can do this by keeping the chart type as stacked and adding a separate helper chart series that is invisible and pushes the other series up. Then it is all about calculating the different values for this helper chart series based on the previous values of the actual chart series.
By invisible, I imply making both the line and the fill none. The chart series will still be there, but you just won't see it unless you start editing the chart.
I created waterfall chart from Stacked column chart and used the Workbook object from the Chartdata of the Selected shape.
You can follow this link to check how to create waterfall chart manually..and then manipulate the Worksheet/datasheet of the chart through code to create Waterfall chart.
Here is a snippet of my code..
private void calculationAndFormatting(bool excelEvent,Excel.Worksheet Sheet)
{
//unregister from Excel Change Event
Sheet.Application.EnableEvents = false;
int lRow = 1;
lRow = iRowCount;
Sheet.Range["A1", "A" + lRow].Copy(Type.Missing);
Sheet.Range["B" + (lRow + 5)].PasteSpecial(Excel.XlPasteType.xlPasteAll, Excel.XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);
Sheet.Range["B1", "B" + lRow].Copy(Type.Missing);
Sheet.Range["A" + (lRow + 5)].PasteSpecial(Excel.XlPasteType.xlPasteAll, Excel.XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);
Sheet.Range["B1", "D1"].Copy(Type.Missing);
Sheet.Range["C" + (lRow + 5)].PasteSpecial(Excel.XlPasteType.xlPasteAll, Excel.XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);
int ulRow = Sheet.UsedRange.Rows.Count;
int k=ulRow;
if (!excelEvent)
{
Sheet.Range["C" + (lRow + 6)].Value = "0";
Sheet.Range["C" + (ulRow + 1)].Value = "0";
Sheet.Range["A" + (ulRow + 1)].Value = "=SUM(A" + (lRow + 5) + ":" + "A" + ulRow + ")";
Sheet.Range["B" + (ulRow + 1)].Value = "Total Value";
k = ulRow + 1;
}
for (int i = lRow + 6; i <= k; i++)
{
if (Sheet.Range["A" + i].Value < 0)
{
if (i <= k-1 && i != lRow + 6)
{
Sheet.Range["C" + i].Formula = "=E" + (i - 1) + "+" + "A" + i;
}
Sheet.Range["D" + i].Formula = "=-A" + i;
Sheet.Range["E" + i].Formula = "=C" + i;
}
else
{
if (i <= k-1 && i != lRow + 6)
{
Sheet.Range["C" + i].Formula = "=E" + (i - 1);
}
Sheet.Range["D" + i].Formula = "=A" + i;
Sheet.Range["E" + i].Formula = "=C" + i + "+" + "D" + i;
}
}
string sourceCol = "='" + Sheet.Name + "'!$C$" + (lRow + 5) + ":$E$" + (k);
pChart.SetSourceData(sourceCol, PowerPoint.XlRowCol.xlColumns);
PowerPoint.Axis axis = pChart.Axes(PowerPoint.XlAxisType.xlValue);
axis.MaximumScale = 1.25 * (System.Double)Sheet.Range["D" + (k)].Value;
axis.MinimumScale = 0.0;
PowerPoint.Axis catAxis = pChart.Axes(PowerPoint.XlAxisType.xlCategory);
catAxis.CategoryNames = Sheet.Range["B" + (lRow + 6), "B" + (k)].Value;
applyFormatting();
drawLeaderLines(Sheet);
Sheet.Application.EnableEvents = true;
Sheet = null;
}
public void writeToCard2(string sourceText, string cardType)
{
Cursor.Current = Cursors.WaitCursor;
int itemLength = sourceText.Split(',').Length;
sourceText = itemLength.ToString() + "," + sourceText + ",";
byte[] dataByteArray = Encoding.GetEncoding(932).GetBytes(sourceText);
//textBox2.Text = BitConverter.ToString(dataByteArray);
int dataByteLength = dataByteArray.Length;
int writeLength = dataByteLength + 11;
byte[] writeByteArray = new byte[writeLength];
writeByteArray[0] = 0x02;//STX
writeByteArray[1] = 0x00;//アドレス
writeByteArray[2] = 0x78;//コマンド
writeByteArray[3] = Convert.ToByte(dataByteLength + 4);//データ長
writeByteArray[4] = 0xa1;//詳細コマンド
writeByteArray[5] = 0x00;//書き込み開始ブロック番号
writeByteArray[6] = Convert.ToByte(dataByteLength);//書き込みバイト数
for (int i = 0; i < dataByteLength; i++)
{
writeByteArray[i + 7] = dataByteArray[i];//書き込みデータ
}
writeByteArray[dataByteLength + 7] = 0x40;//オプションフラグ
writeByteArray[dataByteLength + 8] = 0x03;//ETX
byte sum = 0x00;
for (int i = 0; i <= dataByteLength + 8; i++)
{
sum += writeByteArray[i];
}
writeByteArray[dataByteLength + 9] = sum;//SUM値
writeByteArray[dataByteLength + 10] = 0x0d;//CR
//string tempStr = BitConverter.ToString(writeByteArray);
//port.Write(writeByteArray, 0, writeByteArray.Length);
serialPort1.Write(writeByteArray, 0, writeByteArray.Length);
writeCardType = cardType;
Cursor.Current = Cursors.Default;
}
the above method writes data on an rfid tag in the line
serialPort1.Write(writeByteArray, 0, writeByteArray.Length);
writeByteArray is the size of the data which is exceeding the limit of the RFID tag, my boss said convert it to ascii code and then write to RFID.
Will this help can this conversion reduce size of data?
Is there any other way round withoud using a different RFID tag?
Your boss said to convert to ASCII cause device reads information byt per byte. I worked with that devices and that is usual way they read the data stream passed to them.
There is not any allocation benefit in this, cause the size of the data remains the same, what changes is information rapresentation. That is.