PhP Convert a multidimensional array column by column? - php

I'm trying to build a google chart column chart where each column represents a property (house), and the income, per year of that property. The year would be represented on the x-axis, and the y-axis represents the income amount.
End result in JS would need to look like this:
var columnChartData = google.visualization.arrayToDataTable([
['Year', 'Property 1', 'Property 2'],
['2022', 300, 4000],
['2023', 6000, 8000]
]);
I'm currently struggling with converting my PHP array below, to the required google format. How can I build an array column by column?
array([Property 1] => Array ( [2021] => 353.93 [2022] => 12628.65 [2023] => 12841.57 )
[Property 2] => Array ( [2022] => 370.78 [2023] => 12841.57 ))
required JS/GoogleChart:
['Year', 'Property 1', 'Property 2'],
['2022', 300, 4000],
['2023', 6000, 8000]

Here is one approach. This heavily relies on the assumption that for each property, a record for the Year is included. For example, we are assuming that the nth element of Property1 has the same year as the nth element of Property2.
Note, if OP controls the source with SQL or some other data source, it would be much easier to use PIVOT which is available in many RDBMS (but notably not mySQL). Likewise, using a charting framework like plotly might be helpful as well - I like the examples from this website which shows us using two separate data sources on the same plot area. We could imagine applying this by having a dataset for Property1 and a separate one for Property2.
https://plotly.com/javascript/line-charts/
<?php
$data = ["Property1" => [2022=>300, 2023=>6000], "Property2" => [2022=>4000, 2023=>8000]];
// Reshape so that each row consists of [Year, Property1, Property2]
$formattedArray = array_map(function ($year, $prop1, $prop2) {
return [$year, $prop1, $prop2];
},
array_keys($data["Property1"]),
array_values($data["Property1"]),
array_values($data["Property2"]));
// Add a header
$res = array_merge([["Year", "Property1", "Property2"]], $formattedArray);
// Method 2
$keys = array_keys($data);
$res2 = [array_merge(["Year"], $keys)];
// assume each property has the same years
$years = array_keys($data[$keys[0]]);
for ($i = 0; $i<count($years); $i++) {
$thisRow = [$years[$i]];
foreach($data as $prop) {
array_push($thisRow, array_values($prop)[$i]);
}
array_push($res2, $thisRow);
}
print_r($res2);
?>
<html>
<head> <!--Load the AJAX API-->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
let thisData = <?php echo json_encode($res);?>;
// Load the Visualization API and the corechart package.
google.charts.load('current', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the scatter chart, passes in the data and
// draws it.
function drawChart() {
// Create the data table.
<?php echo "var chartData = google.visualization.arrayToDataTable(" . json_encode($res2) . ");"?>
// Set chart options
var options = {'title':'Different Properties By Year',
'width':600,
'height':400};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.ScatterChart(document.getElementById('chart_div'));
chart.draw(chartData, options);
}
</script>
</head>
<body>
<!--Div that will hold the pie chart-->
<div id="chart_div"></div>
</body>
</html>

Related

Use Google Spreadsheet API to obtain values filtered by Filter view

I'm trying to use the Google Sheets API (in PHP using google/apiclient and OAuth 2.0) to get data from a spreadsheet.
This spreadsheet has defined some "Filter views" (defined in Data > Filter views) where for example one of the filter display only rows where the Price column is greater than x.
I'm trying to find a method to get already filtered data using one of the existing Filter views, but I can't find it. Is there any way to do it?
Although I'm not sure about your current script from your question, when you want to retrieve the filtered values from the sheet using the filter view, unfortunately, in the current stage, there are no methods for directly achieving it. So, in this case, it is required to use a workaround. The flow of the workaround is as follows.
Flow of this workaround:
Retrieve the settings of the filter view (filterViews) you want to use.
In this case, the method of "spreadsheets.get" can be used.
Create new basic filter to the sheet you want to use using the retrieved settings of the filter view.
In this case, the method of "spreadsheets.batchUpdate" can be used.
Retrieve the values of rowMetadata of the sheet.
In this case, the method of "spreadsheets.get" can be used.
At the values of rowMetadata, the filtered rows have the property of "hiddenByFilter": true,. Using this, you can retrieve the hidden rows and/or the showing rows.
Delete the created basic filter.
By above flow, the filtered values can be retrieved.
Sample script:
When you have already been able to get and put values for Google Spreadsheet using Sheets API with googleapis for PHP, as a sample script of above workaround, you can use the following script.
$service = new Google_Service_Sheets($client); // Please use $client from your script.
$spreadSheetId = '###'; // Please set the Spreadsheet ID.
$sheetName = 'Sheet1'; // Please set the sheet name.
$filterViewName = 'sampleFilter1'; // Please set the filter view name.
// 1. Retrieve the settings of the filter view (`filterViews`) you want to use.
$sheets = $service->spreadsheets->get($spreadSheetId, ["ranges" => [$sheetName], "fields" => "sheets"])->getSheets();
$sheetId = $sheets[0]->getProperties()->getSheetId();
$filterViews = $sheets[0]->getFilterViews();
$filterView = array();
foreach ($filterViews as $i => $f) {
if ($f->getTitle() == $filterViewName) {
array_push($filterView, $f);
};
};
if (count($filterView) == 0) return;
// 2. Create new basic filter to the sheet you want to use using the retrieved settings of the filter view.
$obj = $filterView[0];
$obj['range']['sheetId'] = $sheetId;
$requests = [
new Google_Service_Sheets_Request(['clearBasicFilter' => ['sheetId' => $sheetId]]),
new Google_Service_Sheets_Request([
'setBasicFilter' => [
'filter' => [
'criteria' => $obj['criteria'],
'filterSpecs' => $obj['filterSpecs'],
'range' => $obj['range'],
'sortSpecs' => $obj['sortSpecs'],
]
]
])
];
$batchUpdateRequest = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest(['requests' => $requests]);
$service->spreadsheets->batchUpdate($spreadSheetId, $batchUpdateRequest);
// 3. Retrieve the values of `rowMetadata` of the sheet.
$sheets = $service->spreadsheets->get($spreadSheetId, ["ranges" => [$sheetName], "fields" => "sheets"])->getSheets();
$rowMetadata = $sheets[0]->getData()[0]->getRowMetadata();
$filteredRows = array(
'hiddenRows' => array(),
'showingRows' => array()
);
foreach ($rowMetadata as $i => $r) {
if (isset($r['hiddenByFilter']) && $r['hiddenByFilter'] === true) {
array_push($filteredRows['hiddenRows'], $i + 1);
} else {
array_push($filteredRows['showingRows'], $i + 1);
};
};
// 4. Delete the created basic filter.
$requests = [new Google_Service_Sheets_Request(['clearBasicFilter' => ['sheetId' => $sheetId]])];
$batchUpdateRequest = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest(['requests' => $requests]);
$service->spreadsheets->batchUpdate($spreadSheetId, $batchUpdateRequest);
print($filteredRows);
Result:
When above script is used for the following sample Spreadsheet,
Before filter view is not set.
After filter view was set.
Result value
From above Spreadsheet, the following result is obtained.
{
"hiddenRows": [2, 3, 5, 6, 8, 9],
"showingRows": [1, 4, 7, 10, 11, 12, 13, 14, 15]
}
hiddenRows is the hidden row numbers.
showingRows is the showingRows row numbers.
Note:
IMPORTANT: In this sample script, when the basic filter is used in the sheet, the basic filter is cleared. Please be careful this. When you test this script, please use the sample Spreadsheet.
In this sample script, the hidden rows and showing rows are retrieved. Using these values, you can retrieve the filtered values.
References:
Method: spreadsheets.get
Method: spreadsheets.batchUpdate
Related thread.
Is there a way to check if a row is hidden by a filter view in Google Sheets using Apps Script?
This thread is for Google Apps Script. Unfortunately, I couldn't find the PHP script for this. So I added the sample script.

PHP - Google Charts

I'm trying to use Google Charts and i'm having some trouble to create array for API.
https://developers.google.com/chart/interactive/docs/gallery/linechart#creating-material-line-charts
My source of data has 3 columns, which is quantity, date and user name.
(you can see in attachment).
How to convert his form of array to use with google charts api (line charts)?
I think i need to convert each different value from column "responsavel" to a new column and set these column the value of column "qt_pedido"
In this example, the final array should be something like this:
Date | user1 | user2
-------------------------------
03/09/2018 | 58 | 19
05/09/2019 | 23 | 5
Result from sql server
it would be easier to use google data table methods to transform the rows to columns,
see following working snippet...
google.charts.load('current', {
packages: ['line']
}).then(function () {
// create data table
var data = google.visualization.arrayToDataTable([
['Date', 'responsavel', 'qt_pedido'],
['20/10/2018', 'user1', 10],
['20/10/2018', 'user2', 20],
['20/10/2018', 'user3', 30],
['20/11/2018', 'user1', 40],
['20/11/2018', 'user2', 50],
['20/11/2018', 'user3', 60],
['20/12/2018', 'user1', 70],
['20/12/2018', 'user2', 80],
['20/12/2018', 'user3', 90],
]);
// create data view
var view = new google.visualization.DataView(data);
// column arrays
var aggColumns = [];
var viewColumns = [0];
// build view & agg columns for each responsibility
data.getDistinctValues(1).forEach(function (responsibility, index) {
viewColumns.push({
calc: function (dt, row) {
if (dt.getValue(row, 1) === responsibility) {
return dt.getValue(row, 2);
}
return null;
},
label: responsibility,
type: 'number'
});
aggColumns.push({
aggregation: google.visualization.data.sum,
column: index + 1,
label: responsibility,
type: 'number'
});
});
// set view columns
view.setColumns(viewColumns);
// sum view by date
var aggData = google.visualization.data.group(
view,
[0],
aggColumns
);
// draw chart
var chart = new google.charts.Line(document.getElementById('chart_div'));
chart.draw(aggData);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Exclude value if under a certain percentage

I am trying to make a pie chart, using chartjs, to display almost all the data in a database. What it does is count how many times a certain word is used in the database and it uses that count as the value and the word as the name.
In my pie chart I have almost 10 values that are about .01% of the pie chart and i'm trying to remove them.
Anyone have any ideas?
Also google searching this makes it seem like chartjs is not that popular nor supported, would it be more reasonable to use something else?
EDIT:
cakephp MODEL:
function pieChart($conditions = null) {
//Get Data for PieChart
$this->RecordDrug->virtualFields['sum'] ='COUNT(*)';
$records = array();
$records=$this->RecordDrug->find('list',
array(
'conditions' => $conditions,
'fields' => array( 'Drug.drug', 'sum'),
'contain' => array( 'Drug', 'Record' ),
'group' => 'Drug.Drug'
));
return $records;
}
CONTROLLER:
$pieChart = $this->Record->pieChart();
$this->set('output',$pieChart);
VIEW:
var pieChartDataSource = [
<?php
foreach($output as $compound => $sum){
echo "{category: '".$compound."', value: ".$sum."}, ";
}
?>
];
I think what you need is to filter your dataset before calling chartjs to create the chart. Is there any reason not to do that?
$sumofvars=array_sum($output);
$above_percent=0.1;
foreach($output as $compound => $sum){
$percentage_to_eliminate=($sum/$sumofvars)*100;
if ($percentage_to_eliminate>$above_percent)
{
echo "{category: '".$compound."', value: ".$sum."}, ";
}
}
?>
];

How can I fetch all items from a DynamoDB table without specifying the primary key?

I have a table called products with primary key Id. I want to select all items in the table. This is the code is I'm using:
$batch_get_response = $dynamodb->batch_get_item(array(
'RequestItems' => array(
'products' => array(
'Keys' => array(
array( // Key #1
'HashKeyElement' => array( AmazonDynamoDB::TYPE_NUMBER => '1'),
'RangeKeyElement' => array( AmazonDynamoDB::TYPE_NUMBER => $current_time),
),
array( // Key #2
'HashKeyElement' => array( AmazonDynamoDB::TYPE_NUMBER => '2'),
'RangeKeyElement' => array( AmazonDynamoDB::TYPE_NUMBER => $current_time),
),
)
)
)
));
Is it possible to select all items without specifying the primary key? I'm using the AWS SDK for PHP.
Amazon DynamoDB provides the Scan operation for this purpose, which returns one or more items and its attributes by performing a full scan of a table. Please be aware of the following two constraints:
Depending on your table size, you may need to use pagination to retrieve the entire result set:
Note
If the total number of scanned items exceeds the 1MB limit, the
scan stops and results are returned to the user with a
LastEvaluatedKey to continue the scan in a subsequent operation. The
results also include the number of items exceeding the limit. A scan
can result in no table data meeting the filter criteria.
The result set is eventually consistent.
The Scan operation is potentially costly regarding both performance and consumed capacity units (i.e. price), see section Scan and Query Performance in Query and Scan in Amazon DynamoDB:
[...] Also, as a table grows, the scan operation slows. The scan
operation examines every item for the requested values, and can use up
the provisioned throughput for a large table in a single operation.
For quicker response times, design your tables in a way that can use
the Query, Get, or BatchGetItem APIs, instead. Or, design your
application to use scan operations in a way that minimizes the impact
on your table's request rate. For more information, see Provisioned
Throughput Guidelines in Amazon DynamoDB. [emphasis mine]
You can find more details about this operation and some example snippets in Scanning Tables Using the AWS SDK for PHP Low-Level API for Amazon DynamoDB, with the most simple example illustrating the operation being:
$dynamodb = new AmazonDynamoDB();
$scan_response = $dynamodb->scan(array(
'TableName' => 'ProductCatalog'
));
foreach ($scan_response->body->Items as $item)
{
echo "<p><strong>Item Number:</strong>"
. (string) $item->Id->{AmazonDynamoDB::TYPE_NUMBER};
echo "<br><strong>Item Name: </strong>"
. (string) $item->Title->{AmazonDynamoDB::TYPE_STRING} ."</p>";
}
Hi you can download using boto3. In python
import boto3
from boto3.dynamodb.conditions import Key, Attr
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Table')
response = table.scan()
items = response['Items']
while 'LastEvaluatedKey' in response:
print(response['LastEvaluatedKey'])
response = table.scan(ExclusiveStartKey=response['LastEvaluatedKey'])
items.extend(response['Items'])
I figured you are using PHP but not mentioned (edited). I found this question by searching internet and since I got solution working , for those who use nodejs here is a simple solution using scan :
var dynamoClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: config.dynamoClient.tableName, // give it your table name
Select: "ALL_ATTRIBUTES"
};
dynamoClient.scan(params, function(err, data) {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("GetItem succeeded:", JSON.stringify(data, null, 2));
}
});
I assume same code can be translated to PHP too using different AWS SDK
I fetch all items from dynamodb with the following query. It works fine. i create these function generic in zend framework and access these functions over the project.
public function getQuerydata($tablename, $filterKey, $filterValue){
return $this->getQuerydataWithOp($tablename, $filterKey, $filterValue, 'EQ');
}
public function getQuerydataWithOp($tablename, $filterKey, $filterValue, $compOperator){
$result = $this->getClientdb()->query(array(
'TableName' => $tablename,
'IndexName' => $filterKey,
'Select' => 'ALL_ATTRIBUTES',
'KeyConditions' => array(
$filterKey => array(
'AttributeValueList' => array(
array('S' => $filterValue)
),
'ComparisonOperator' => $compOperator
)
)
));
return $result['Items'];
}
//Below i Access these functions and get data.
$accountsimg = $this->getQuerydataWithPrimary('accounts', 'accountID',$msgdata[0]['accountID']['S']);
A simple code to list all the Items from DynamoDB Table by specifying the region of AWS Service.
import boto3
dynamodb = boto3.resource('dynamodb', region_name='ap-south-1')
table = dynamodb.Table('puppy_store')
response = table.scan()
items = response['Items']
# Prints All the Items at once
print(items)
# Prints Items line by line
for i, j in enumerate(items):
print(f"Num: {i} --> {j}")
Here is an example for Java. In withAttributesToGet you specify what exactly you want to read. Before run you have to place credential file to your .aws folder.
public static final String TABLE_NAME = "table_name";
public static final AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(Regions.CA_CENTRAL_1)
.build();
public static void main(String[] args) throws IOException, InterruptedException {
downloadAllRecords();
}
public static void downloadAllRecords() throws InterruptedException, IOException {
final Object[] FILE_HEADER = {"key", "some_param"};
CSVFormat csvFormat = CSVFormat.DEFAULT.withRecordSeparator("\n");
CSVPrinter csvPrinter = new CSVPrinter(new FileWriter(TABLE_NAME + ".csv"), csvFormat);
csvPrinter.printRecord(FILE_HEADER);
ScanRequest scanRequest = new ScanRequest()
.withTableName(TABLE_NAME)
.withConsistentRead(false)
.withLimit(100)
.withAttributesToGet("key", "some_param");
int counter = 0;
do {
ScanResult result = client.scan(scanRequest);
Map<String, AttributeValue> lastEvaluatedKey = result.getLastEvaluatedKey();
for (Map<String, AttributeValue> item : result.getItems()) {
AttributeValue keyIdAttribute = item.getOrDefault("key", new AttributeValue());
AttributeValue createdDateAttribute = item.getOrDefault("some_param", new AttributeValue());
counter++;
List record = new ArrayList();
record.add(keyIdAttribute.getS());
record.add(createdDateAttribute.getS());
csvPrinter.printRecord(record);
TimeUnit.MILLISECONDS.sleep(50);
}
scanRequest.setExclusiveStartKey(lastEvaluatedKey);
} while (scanRequest.getExclusiveStartKey() != null);
csvPrinter.flush();
csvPrinter.close();
System.out.println("CSV file generated successfully.");
}
Also specify necessary dependencies.
<dependencies>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.5.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sparkjava/spark-template-velocity -->
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-template-velocity</artifactId>
<version>2.7.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-logs -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-logs</artifactId>
<version>1.12.132</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-dynamodb -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>1.12.161</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
Example of credential file
[default]
aws_access_key_id = AAAAAAA
aws_secret_access_key = AAAAAAAA
aws_session_token = AAAAAAA
I'm not specifying a pk on following code:
client = boto3.client('dynamodb')
table = 'table_name'
response = client.scan(
TableName=table,
AttributesToGet=['second_field_in_order', 'first_field_in_order']
)
This C# code is to fetch all items from a dynamodb table using BatchGet or CreateBatchGet
string tablename = "AnyTableName"; //table whose data you want to fetch
var BatchRead = ABCContext.Context.CreateBatchGet<ABCTable>(
new DynamoDBOperationConfig
{
OverrideTableName = tablename;
});
foreach(string Id in IdList) // in case you are taking string from input
{
Guid objGuid = Guid.Parse(Id); //parsing string to guid
BatchRead.AddKey(objGuid);
}
await BatchRead.ExecuteAsync();
var result = BatchRead.Results;
// ABCTable is the table modal which is used to create in dynamodb & data you want to fetch

Retrieving data from Google APIs and populate twice

I need your help because I'm stuck on how to get secondary datas from Google API and show them on a graph, let me explain :
I've successfully retrieved "visits" datas from the Google Analytics API and show them on my chart with this code :
$report3 = $ga->getReport(
array('dimensions'=>urlencode('ga:date'),
'metrics'=>urlencode('ga:pageviews,ga:visits'),
'filters'=>urlencode(''),
'sort'=>'ga:date'
)
);
Which give me an array like :
[20110726] => Array
(
[ga:pageviews] => 0
[ga:visits] => 0
)
Then I put these informations on the chart with :
<script type='text/javascript'>
var ga_data = [
<?php
$str = "";
foreach ($report3 as $value)
$str.="{$value["ga:visits"]},";
$str = rtrim($str,",");
echo $str;
?>
];
</script>
And finally :
// Create the data table.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Month');
data.addColumn('number', 'visits');
data.addRows(ga_data.length*1);
for (i=9; i<ga_data.length; i++){
data.setValue(i, 0, 'DATE NEEDED');
data.setValue(i, 1, ga_data[i]);
}
The only problem is that I need to show the DATE : ([20110726] => Array
(
[ga:pageviews] => 0
[ga:visits] => 0
)) on the chart like I do with visits
Any help will be very very appreciated ! Thanks !!!
Looks like the data coming back has the date as the index of the array (a.k.a. 'Dimension'). Just update your loop to get that like so.
foreach(ga_data as $date=>$values){
data.setValue(i, 0, $date);
data.setValue(i, 1, $values['ga:visits']);
}
You might need to split the date into its parts if the DataVisualization API can't handle that type like so:
$str = '20110726';
$year = substr($str,0,4);
$month = substr($str,4,2);
$day = substr($str,6,2);
Then format to your liking with the PHP date() function.

Categories