Create a dictionary (array) from CSV data [duplicate] - php

This question already has an answer here:
Iterating through a column in a CSV file (PHP)
(1 answer)
Closed 9 hours ago.
I need to write a function that takes temperature as an input and returns a dictionary with years as keys and number of days as values.
CSV file looks like this (year, month, day, hour, temperature):
2019,1,1,0,0.1
2019,1,1,1,0.4
2019,1,1,2,0.8
2019,1,1,3,1.3
2019,1,1,4,1.8
...
2020,1,1,0,-3.9
The number of days is calculated by another function which I already have. It takes a year and a temperature and returns how many days in a given year the temperature was equal to or below the given temperature. Since the data is about hours, not days, the number of hours is found and then divided by 24.
The function:
function getDaysUnderTemp(int $targetYear, float $targetTemp): float {
$file = fopen("data/temperatures-filtered.csv", "r");
$hours = 0;
while ($data = fgetcsv($file)) {
if ($data[0] == $targetYear and $data[4] <= $targetTemp) {
$hours ++;
}
}
fclose($file);
return $hours / 24;
}
So as an example getDaysUnderTemp(2019, -10) returns 13.92.
This is a function I am asking about as I'm not sure how it might be done:
function getDaysUnderTempDictionary(float $targetTemp): array {
$file = fopen("data/temperatures-filtered.csv", "r");
while ($data = fgetcsv($file)) {
???
}
fclose($file);
return [];
}
The problem is I don't understand how an already written function could be implemented in this new one, and then create a required dictionary from all this data. Without using classes.
Desired output of getDaysUnderTempDictionary(-10):
Array
(
[2019] => 3.88
[2020] => 0.21
[2021] => 13.92
)

Instead of just storing hours as a single variable, store it in an array indexed by year. Once done with the CSV, loop over the array and divide each by 24, same as you did before.
This code has a mock CSV file for demo purposes, but is otherwise the same as yours:
function getDaysUnderTempDictionary(float $targetTemp): array {
//This is just for mocking a CSV file
$dataString = <<<EOT
2019,1,1,0,0.1
2019,1,1,1,0.4
2019,1,1,2,0.8
2019,1,1,3,1.3
2019,1,1,4,1.8
2020,1,1,0,-3.9
EOT;
$stream = fopen('php://memory', 'r+');
fwrite($stream, $dataString);
rewind($stream);
$years = [];
while ($data = fgetcsv($stream)) {
$year = $data[0];
if ($data[4] <= $targetTemp) {
if(!isset($years[$year])){
$years[$year] = 0;
}
$years[$year]++;
}
}
foreach($years as $year => $hours){
$years[$year] = $hours / 24;
}
return $years;
}
Demo: https://3v4l.org/RWFPK

Related

Iterating through a column in a CSV file (PHP)

I need to write a function that takes a year and a temperature as an input and returns how many days in a given year the temperature was equal to or below the given temperature. Since the data is about hours, not days, need to find the number of hours and divide it by 24.
Example: getDaysUnderTemp(2019, -10) returns 13.92.
CSV file looks like this (year, month, day, hour, temperature):
2019,1,1,0,0.1
2019,1,1,1,0.4
2019,1,1,2,0.8
2019,1,1,3,1.3
2019,1,1,4,1.8
...
2020,1,1,0,-3.9
So far my code looks like this (I'm new to php):
function getDaysUnderTemp(int $targetYear, float $targetTemp): float {
$inputFile = fopen("data/temperatures-debug.csv", "r");
while (!feof($inputFile)) {
$file = fgetcsv($inputFile);
$hours = intval($file[3]);
if (intval($file[0]) === $targetYear and floatval($file[4]) <= $targetTemp) {
???
return ??? / 24;
}
fclose($inputFile);
}
return "error";
}
The problem is I don't know how to iterate through a column which represents the hours of a specific year, and then add them all together.
you are not far from the solution.
function getDaysUnderTemp($targetYear, $targetTemp): float
{
$hours = 0;
if ((($handle = fopen("data.csv", "r")) !== FALSE)) {
while (($data = fgetcsv($handle)) !== FALSE) {
// [0] -> year [1] -> month [2] -> day [3] -> hours [4] -> temp
if ($data[0] == $targetYear && $data[4] <= $targetTemp) {
$hours += $data[3];
}
}
fclose($handle);
}
return $hours / 24;
}
The logic you had was correct, I just refactored the code and fixed some small issues:
Your function is bound to return a float value but since in your code you are returning the string "error" when a match is not found It would have thrown an exception at some point. I am now returning $hours / 24 so now the problem is solved;
After each iteration, you close the pointer to file by calling fclose($inputFile), this can cause some problems since the feof function needs a valid stream not closed to work properly. Now the fclose function is called only once when the work is done;
For printing the results I suggest you to use the printf function as follows:
printf("%.2f", getDaysUnderTemp(2019, -10);

How to subtract time from time array using php?

Hello seniors I have a question related to some PHP script.
I have an array containing time => ['12:10', '4:16', '2:5'] and have one html form containing input field of type 'number'.
I want when I enter some value in my form input field for example I enter 7, so after submitting the form in back-end the number 7 which i enter in input field is subtracted from the array which I mentioned above and I will get the result array like:
['5:10', '4:16', '2:5']
I have tried something like that but not able to implement my logic
$val = array(1, 0, 2, 1, 1);
$subtract = 3.5;
foreach ($val as $key => $item) {
if ($subtract >= $item) {
$subtract -= $item;
$val[$key] = 0;
} else {
$val[$key] -= $subtract;
$subtract = 0;
}
}
Any kind of help is highly appreciated
You can use Carbon library for date/time manipulation:
<?php
use Carbon\Carbon;
$times = ['17:46', '03:05', '21:56'];
$timeshift = 3;
$new_times = array_map(
fn($t) => Carbon::createFromFormat('H:i', $t)
->subHours($timeshift)
->format('H:i'),
$times
);
Test Carbon library online
No need for library, just convert your first array to seconds: 1 hour = 3600 ; 1 minute = 60 ; 12:10 is 12 x 3600 + 10 x 60, then you do the same thing to your $_POST value, then use gmdate() to retrieve the original format of your array
$myTimes=array('12:10', '4:16', '2:5');
//do the math
$splittedTime = explode(":", $myTimes[0]); //in your case
$timeInSeconds = $splittedTime[0] * 3600 + $splittedTime[1] * 60 ;
//do the same thing to your your $_POST value if needed or simply
$totalReduceby = 7 * 3600;
// get new total of seconds
$newTime= $timeInSeconds - $totalReduceby;
$result = ltrim(gmdate("H:i", $newTime),0); //ltrim to remove the leading 0
$myTimes=array($result, '4:16', '2:5');
//print_r($myTimes);
time => ['12:10', '4:16', '2:5']
[...]
the number 7 which i enter in input field is subtracted from the array
I will get the result array like: ['5:10', '4:16', '2:5']
Your example is a little ambiguous. Do you only want to subtract the field value from the first element of the array, always? Or only from those elements which are greater than the submitted value?
It's pretty straightforward to subtract minutes from a mm:ss time string; simplest is probably to generalize so that the amount to subtract is also allowed to be mm:ss instead of always being a whole number of minutes. I would just explode both of them, turn them into total seconds (minutes*60+seconds), subtract those, and then turn back into mm:ss. Both conversions might be worth their own functions:
function mmssToSeconds($timeStr) {
if (str_contains($timeStr, ':')) {
list($min, $sec) = explode(':', $timeStr);
} else {
list($min, $sec) = array($timeStr, 0);
}
if ($min < 0) {
return 60*$min - $sec;
} else {
return 60*$min + $sec;
}
}
function secondsToMmss($seconds) {
$abs = abs($seconds);
$sgn = $seconds / $abs;
$min = floor($abs / 60);
$sec = $abs % 60;
return ($sgn < 0 ? '-' : '').sprintf('%d:%02d', $min, $sec);
}
And then the subtraction is easy:
function subtractMinutes($from, $delta) {
return secondsToMmss(mmssToSeconds($from) - mmssToSeconds($delta));
}
If you want to subtract from each element that is big enough, you could use a loop like this:
foreach ($ary['time'] as $i => $t) {
if ((int)$t > $subtract) {
$ary['time'][$i] = subtractMinutes($t, $subtract);
}
}
The comparison works because the cast from string to int ignores everything after the first non-digit, so '12:10' just becomes 12, which is > 7.

Checking array for duplicate of 2 objects

I have a small program sunning that reads in a csv file with customers and their bookings and places them into a nested array, I have already sorted some of the data, but now need to make sure that there isn't any customers booking for the same date and time (also is it possible to check that the time is at least 30 min apart, all the times are in 30 min increments) I've tried a simple in_array method and got nothing tried a few other ways and didn't get any where, any ideas how I could do this. $date[5] is the dates and $data[6] is the time, the data is all strings.
if (($h = fopen("{$filename}", "r")) !== FALSE) {
//each line in the file is converted into its own individual array
while (($data = fgetcsv($h, 1000, ",")) !==FALSE) {
//check if anyone has tried to book for unavailable dates, weekends, last day of the month etc
if (!in_array($data[5], $Adates)) {
$contact[] = $data;
}
//this checks if the person is trying to make a booking before 9am or after 6pm
elseif ((($data[6]) < "09:00") || (($data[6]) > "18:00")) {
$contact[] = $data;
} else {
$College1[] = $data;
}
if (in_array($data[5], $College1)
&& in_array($data[6],$College1)) {
$College2[] = $College1;
}
Heres a sample of some of the test data i used (there is over 30 customers) :
T1,Aleshia,Tomkiewicz,atomkiewicz#hotmail.com,01835-703597,03/11/2020,11:30,P256
T2,Evan,Zigomalas,evan.zigomalas#gmail.com,01937-864715,12/11/2020,15:00,P146
T3,France,Andrade,france.andrade#hotmail.com,01347-368222,30/11/2020,08:30,P68
In regards to your code, I guess you could improve your elseif manipulating the time string, getting just the hour and comparing that number as an int.
elseif ((($data[6]) < "09:00") || (($data[6]) > "18:00"))
$hrBooked = (int) explode(':', $data[6])[0]; // This should be placed before the main if
elseif (( $hrBooked < 9) || $hrBooked > 18))
Looking at your code, I think you'll benefit from using the DateTime PHP class for all your checks and comparisons.

working with data in TXT file

I have a text file that contain data like this (EURUSD quotes)
19710104,000000,0.53690,0.53690,0.53690,0.53690,1
19710105,000000,0.53660,0.53660,0.53660,0.53660,1
19710106,000000,0.53650,0.53650,0.53650,0.53650,1
19710107,000000,0.53680,0.53680,0.53680,0.53680,1
19710108,000000,0.53710,0.53710,0.53710,0.53710,1
19710111,000000,0.53710,0.53710,0.53710,0.53710,1
19710112,000000,0.53710,0.53710,0.53710,0.53710,1
I want to move some data to another file like
0.53690,0.53690,0.53690,0.53690
and add some difrent calculated number to each line like (Moving average and RSI, Stoch ...) so the file can be trained by Neural Network, final file must be like this
OPEN, HIGH, LOW, CLOSE, VOL, MA50, MA20, RSI14, StochMain, StochSignal,
so I need some hints
You should use the PHP functions fgetcsv and fputcsv. See working example below that you can tweak to your needs.
It assumes that your input values given are in the format OPEN, CLOSE, HIGH, LOW, VOL. Introduce RSI and Stochastic etc in the same way that the Moving Average works.
<?php
// Prepare variables
$row = 1;
$output = array();
// Attempt to open the file quotes.txt
if (($handle = fopen("quotes.txt", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$row++;
// This part of the while loop will be hit for each line in the quotes.txt
// $data is an array that contains the values of this line from the csv
// $output is a new array that you are generating
// Create a new sub-array in the output array (add a new line to the main output)
$output[$row] = array();
// Add the third value of the input array to the start of the new array (the opening price)
$output[$row][] = $data[2];
// Add the fourth value of the input array to the new array (the closing price)
$output[$row][] = $data[3];
// Add the fifth value of the input array to the new array (the high price)
$output[$row][] = $data[4];
// Add the sixth value of the input array to the new array (the low price)
$output[$row][] = $data[5];
// Add the seventh value of the input array to the new array (the volume)
$output[$row][] = $data[6];
// Add moving average to the new array
$output[$row][] = calculate_ma($output, $row);
}
fclose($handle);
}
// Create new file or open existing to save the output to
$handle = fopen('output.csv', 'w');
// Flatten the arrays and save the csv data
foreach ($output as $file) {
$result = [];
array_walk_recursive($file, function($item) use (&$result) {
$result[] = $item;
});
fputcsv($handle, $result);
}
/**
* Calculate the value for the MA using the values in $output.
*/
function calculate_ma($output, $row) {
// For this example we will just say that the MA is equal to the closing price of this period
// and the previous four periods, divided by 5.
$ma = $output[$row][1] + $output[$row-1][1] + $output[$row-2][1] + $output[$row-3][1] + $output[$row-4][1];
$ma = $ma / 5;
return $ma;
}
?>
The output of the above code, using the same input as you have pasted in your question, will be:
0.53690,0.53690,0.53690,0.53690,1,0.10738
0.53660,0.53660,0.53660,0.53660,1,0.2147
0.53650,0.53650,0.53650,0.53650,1,0.322
0.53680,0.53680,0.53680,0.53680,1,0.42936
0.53710,0.53710,0.53710,0.53710,1,0.53678
0.53710,0.53710,0.53710,0.53710,1,0.53682
0.53710,0.53710,0.53710,0.53710,1,0.53692
Bear in mind that the first four moving averages will be incorrect, as they do not have 5 periods of data from which to calculate the 5-period MA.
To calculate a larger MA (50-period) without a huge bunch of code, replace the MA function with:
function calculate_ma($output, $row) {
$period = 50;
for ($x = 0 ; $x < $period ; $x++){
$ma = $ma + $output[$row-$x][1];
}
$ma = $ma / $period;
return $ma;
}
I strongly believe that you should open file, read line by line, explode it with ',' and then store every line to some kind of map, do some calculations and finally save it to another file.
http://php.net/manual/en/function.explode.php
How to read a file line by line in php

Finding the nearest data point and sunshine data in php

I am in a big problem.The problem is following
1)I have a .dat file(dat file used to store the data ).It is a 25 MB file which contains
over 200K rows of latitude and longitudes.Example of one of such row is
-59.083 -26.583 9.4 5.2 3.3 4.3 8.1 6.6 5.3 8.4 8.3 10.0 9.1 5.1
It statrs form the latitude ,longitude,sunshine data (hours) in january,fabruary..and so on to decmber.
I have more than 200K rows like above.
My task is to calculate the sunshine hours in a particulr latitude and longitude.Suppose i have a latitude =3.86082 and longitude=100.50509 ;.So my task would be to find average sunshine hours per month on this latitude and longitude.but the second problem is that i am not going to have exact match of given latitude and longitude with the ones i have in the file.So first of all i have to find the nearest point and then i have to calculate the sunshine hours.
I am using the following code to calculate the nearest point.But it is taking a huge time beacuse of the bulk of data in the file
$file_name='grid_10min_sunp.dat';
$handle = fopen($file_name, "r");
$lat1=13.86082;
$lan1=100.50509;
$lat_lon_sunshines = make_sunshine_dict($file_name);
$closest = 500;
for($c=0;$c<count($lat_lon_sunshines);$c++)
{
$lat2=$lat_lon_sunshines[$c]['lat'];
$lan2=$lat_lon_sunshines[$c]['lan'];
$sunshines=$lat_lon_sunshines[$c]['sunshine'];
$lat_diff = abs(round((float)($lat1), 4)-$lat2);
if ($lat_diff < $closest)
{
$diff = $lat_diff + abs(round((float)($lan1), 4)-$lan2);
if($diff < $closest)
{
$closest = $diff;
$sunshinesfinal=$sunshines;
}
}
$sunshines='';
}
print_r($sunshinesfinal);die;
the function make_sunshine_dict($file_name ) also goes throgh each line of the file and prepares an array as following
$sunshines_dict = array();
$f = file_get_contents($file_name);
$handle = fopen($file_name, "r");
while($buffer = fgets($handle))
{
$tok = strtok($buffer, " \n\t");
$lat=$tok;
$latArray[]=$tok;
$tok = strtok(" \n\t");
$months = '';
$months = array();
for ($k = 0; $tok !== false; $k+=1)
{
if($k==0)
{
$lan=$tok;
$lanArray[]=$tok;
}
if($k!=0)
{
$months[] = $tok ;
"month $k : ".$months[$k]."<br>";
}
$tok = strtok(" \n\t");
}
$data[$kkk]['lat']=$lat;
$data[$kkk]['lan']=$lan;
foreach($months as $m=>$sunshine)
{
$sunshines=array();
$sumD = 0;
$iteration= 31;
for($n=1;$n<=$iteration;$n++)
{
$J = ($m+1)*$n;
$P = asin(.39795*cos(.2163108 + 2*atan(.9671396*tan(.00860*($J-186)))));
$value=(sin(0.8333*pi/180) + sin($lat*pi/180)*sin($P))/(cos($lat*pi/180)*cos($P));
/* $value ? ($value > 1 and 1) : $value;
$value ? ($value < -1 and -1): $value;*/
$D = 24 - ((24/pi) * acos($value));
$sumD = $sumD + $D;
}
$sunshinesdata=(($sumD/30)*(float)($sunshine)*.01);
$data[$kkk]['sunshine'][$m]=$sunshinesdata;
$sunshines='';
}
}
return $data;
Please help and please let me know if you require more information
And please remenber i can not use default php function for sunshine information here beacsue i am also taking cloud cover and other factors into consideration
A lot of the code looks wrong (in terms of just figuring out the sunshine hours). Although, I'll admit, I'm lost as to what you are doing in the make_sunshine_dict function.
In terms of helping you speed things up, you are reading your file twice:
$f = file_get_contents($file_name);
$handle = fopen($file_name, "r");
Also, you can map Lat/Long coordinates into a grid. In other words, make a 2 dimensional array where each row and each column represents 1 degree of latitude and 1 degree of longitude, respectively. As you read in your file, dump entries into the correct lat/long bucket in the grid. When you need to find the location closest to a given lat/lon then you only need to compare that location against the points in its bucket.
Load the .dat into a SQL database every x interval in a Cron job and then just query the database like you always would.

Categories