Function not changing the data frame - r

I've recently made a simple for loop that outputs the Max and Min of the past 5 prices and it works perfectly, creating 2 new columns showing MaxH and MinL:
for(i in 5:nrow(XBTUSD_df_s)){
XBTUSD_df_s$MaxH[i] = max(XBTUSD_df_s$Price[(i-(5-1)):i])
XBTUSD_df_s$MinL[i] = min(XBTUSD_df_s$Price[(i-(5-1)):i])
}
I then put this for loop into a function so that I can adjust how many prices I want the Max and Min to be based off like so (the print lines were added as a sanity check):
FindMaxMin = function(x){
for(i in x:nrow(XBTUSD_df_s)){
XBTUSD_df_s$MaxH[i] = max(XBTUSD_df_s$Price[(i-(x-1)):i])
XBTUSD_df_s$MinL[i] = min(XBTUSD_df_s$Price[(i-(x-1)):i])
print(XBTUSD_df_s$MaxH[i])
print(XBTUSD_df_s$MinL[i])
}
}
But after for example:
FindMaxMin(x = 10)
The console will spit out all the expected results but unlike the for loop by itself, my dataframe will not automatically add on the MaxH and MinL columns.
I've tried return() and I think most likely it is a global environment problem but can't seem to wrap my head around it.
Thanks in advance!

You need to return the object from the function and then assign it later:
FindMaxMin = function(x, XBTUSD_df_s){
for(i in x:nrow(XBTUSD_df_s)){
XBTUSD_df_s$MaxH[i] = max(XBTUSD_df_s$Price[(i-(x-1)):i])
XBTUSD_df_s$MinL[i] = min(XBTUSD_df_s$Price[(i-(x-1)):i])
print(XBTUSD_df_s$MaxH[i])
print(XBTUSD_df_s$MinL[i])
}
return (XBTUSD_df_s)
}
new = FindMaxMin(10, XBTUSD_df_s)

Related

Compare function ( ==, all.equal) is not working properly when it comes to two digit numbers in R?

I am trying to compare two number in my code, when it comes to compare one digit number , it works fine whether I use == or all.equall function, but when it comes to comparing 2 digit number or more like 17, it can't say they are the same, I have already go through this thread and all.equall is not working as well. beside my numbers are all integers. can any one tell me what the problem is here ?
I'll put the code here so the problem can be reproducible.
library(igraph)
node1<- c(1,1,1,2,2,2,3,3,4,4,5,5,7,8,9,9,10,12,14,14,17,17,19)
node2<-c(2,3,4,5,6,17,12,14,7,8,6,13,14,9,10,11,11,13,16,15,18,19,20)
AZADEH_GRAPH.data <- data.frame(node1,node2)
dataframe_AZADEH_GRAPH<-AZADEH_GRAPH
graph_AZADEH_GRAPH=graph.data.frame(dataframe_AZADEH_GRAPH,directed=FALSE)
Nodes1_AZADEH_GRAPH<- replicate(vcount(graph_AZADEH_GRAPH), 0)
SuperEgo_AZADEH_GRAPH<- list()
Com_AZADEH_GRAPH<- list()
community_member <-matrix()
neghbor_list<-list()
count_neighbors<-list()
community_1<-list()
SuperEgo_AZADEH_GRAPH[[2]]=make_ego_graph(graph_AZADEH_GRAPH,2,
V(graph_AZADEH_GRAPH)$name[2],
mode = "all",mindist = 0)
Com_AZADEH_GRAPH[[2]] <- cluster_infomap(SuperEgo_AZADEH_GRAPH[[2]][[1]])
community_member<-data.matrix(membership(Com_AZADEH_GRAPH[[2]]))
neghbor_list[2]=ego(graph_AZADEH_GRAPH, order = 1,
nodes = V(graph_AZADEH_GRAPH)$name[2], mode = "all",mindist = 1)
count_neighbors[2]=length(neghbor_list[[2]])
for (k in 1:nrow(community_member))
{
RRR<-cbind(community_member,as.integer(rownames(community_member)[k]))
}
for (n in 1:nrow(RRR))
{
RRR[n,2]<-as.integer(rownames(RRR)[n])
}
for (i in 1: length(neghbor_list[[2]]))
{
for (j in 1:nrow(RRR))
{
if (neghbor_list[[2]][i]==RRR[[j,2]])
{
community_1[i]=RRR[[j,1]]
}
}
}
the problem is with if statements and more specifically when i=3 and j=6 neghbor_list[[2]][3],
RRR[[6,2]] both return 17 but still it gives False it is working fine when i=1 & 2
(Posted solution on behalf of the question author).
The issue is found, it was referring to the indexes, I should have use $name instead after neghbor_list[[2]][3].

How to include logical checks in a custom function

I have written a custom function that performs a mathematical transformation on a column of data with the inputs being the data and one other input (temperature). I would like to have 2 different logical checks. The first one is whether or not any values in the column exceed a certain threshold, because the transformation is different above and below the threshold. The second is a check if the temperature input is above a certain value and in that case, to deliver a warning that values above the threshold are unusual and to check the data.
Right now, I have the function written with a series of if/else statements. However, this a warning that it is only using the first element of the string of T/F statements. A simplified example of my function is as follows:
myfun = function(temp,data) {
if(temp > 34){
warning('Temperature higher than expected')
}
if (data > 50) {
result = temp*data
return(result)
} else if(data <= 50) {
result = temp/data
return(result)
}
}
myfun(temp = c(25,45,23,19,10), data = c(30,40,NA,50,10))
As you can see, because it is only using the first value for the if/else statements, it does not properly calculate the return values because it doesn't switch between the two versions of the transformation. Additionally, it's only checking if the first temp value is above the threshold. How can I get it to properly apply the logical check to every value and not just the first?
-edit-simplified the function per #The_Questioner's suggestion and changed < 50 to <= 50.
The main issue with your code is that you are passing all the values to the functions as vectors, but then are doing single element comparisons. You need to either pass the elements one by one to the function, or put some kind of vectorized comparison or for loop into your function. Below is the for loop approach, which is probably the least elegant way to do this, but at least it's easy to understand what's going on.
Another issue is that NA's apparently need to be handled in the data vector before passing to any of your conditional statements, or you'll get an error.
A final issue is what to do when data = 50. Right now you have conditional tests for greater or less than 50, but as you can see, the 4th point in data is 50, so right now you get an NA.
myfun = function(temp,data) {
result <- rep(NA,length(temp))
for (t in 1:length(temp)) {
if(temp[t] > 34) {
warning('Temperature higher than expected')
if (!is.na(data[t])) {
if (data [t] > 50) {
result[t] <- temp[t]*data[t]
} else if(data[t] < 50) {
result[t] <- temp[t]/data[t]
}
}
} else {
if (!is.na(data[t])) {
if (data[t] > 50) {
result[t] <- temp[t]*data[t]
} else if(data[t] < 50) {
result[t] <- temp[t]/data[t]
}
}
}
}
return(result)
}
Output:
> myfun(temp = c(25,45,23,19,10), data = c(30,40,NA,50,10))
[1] 0.8333333 1.1250000 NA NA 1.0000000

Format number into K(thousand), M(million) in Shiny DataTables

I'm looking for a straight forward way to change the formatting of numbers into K,M in shiny dataTables. Preferably with something like formatCurrency. I don't want to write k, m functions to convert number into string in order to do the formatting as it makes it difficult to sort rows by value.
There's no built-in way to do this, but it's not too bad to write your own format function in JavaScript that doesn't break row sorting.
See Column Rendering in the DT docs for how to do this: https://rstudio.github.io/DT/options.html
And this will also help:
https://datatables.net/reference/option/columns.render
Here's an example of a custom thousands formatter that rounds to 1 decimal place:
library(DT)
formatThousands <- JS(
"function(data) {",
"return (data / 1000).toFixed(1) + 'K'",
"}")
datatable(datasets::rock, rownames = FALSE, options = list(
columnDefs = list(list(
targets = 0:1, render = formatThousands
))
))
Alternatively, if you want a non-JavaScript method, you could use the colFormat function used with the reactable package. Unfortunately, there is no automatic millions option but it's pretty easy to replicate if you divide the original data and add the labels on with colFormat.
Product <- c('Apples','Oranges','Pears')
Revenue <- c(212384903, 23438872, 26443879)
df <- data.frame(Product,Revenue)
df$Revenue_millions <- dfeg$Revenue/1000000
reactable(df,
showSortable = TRUE,
columns = list(
Revenue_millions = colDef(format = colFormat(prefix = "£", separators = TRUE,digits=1,suffix = "m"))))
The data should now sort correctly
If you are using DataTables, to get the data as Unit format i.e
10000 -> 10K
we can use render function
"render": function ( data ) {
if(data > 999 && data < 1000000) {
return data/1000+' K'
}
else if(data > 1000000){
return data/1000000+' M'
}
else{
return data
}
}
}

Progress bar for rforcecom.checkbatchstatus()

I am asking to write a text or graphical progress tracker while rforcecom's batch update function loads batches of up to 10,000.
To set up and complete a batch update, a few objects must be created--there is no avoiding it. I really do not like having to re-run code in order to check the status of rforcecom.checkBatchStatus(). This needs to be automated while a progress bar gives a visual of actual progress, since checking in the global environment isn't preferred and it will be a static "status" update until it's run again.
Here's how the code is set up:
require(Rforcecom)
## Login to Salesforce using your username and password token
## Once ready to update records, use the following:
job<- rforcecom.createBulkJob(session, operation = 'update',
object = 'custom_object__c')
info<- rforcecom.createBulkBatch(session, jobId = job$id, data = entry,
batchSize = 10000)
### Re-run this line if status(in global environment) is "In Progress" for
### updated status
status<- lapply(info, FUN = function(x) {
rforcecom.checkBatchStatus(session, jobId = x$jobId, batchId = x$id)})
###Once complete, check details
details<- lapply(status, FUN = function(x){
rforcecom.getBatchDetails(session, jobId = x$jobId, batchId = x$id)})
close<- rforcecom.closeBulkJob(session, jobId = job$id)
To automate re-running the status code, use the repeat loop:
repeat {
statements...
if (condition) {
break
}
}
Then, to get a visual for a progress update, use the txtProgressBar() in base R. For this particular function, I made my own progress bar function with two simple companion functions. As a note about progressValue(), the rforcecom.checkBatchStatus() outputs as a list of 1 and a sublist. The sublist name for checking the number of records processed is "numberRecordsProcessed".
progressBar<- function(x, start = 0, finish){
# x is your object that is performing a function over a varying time length
# finish is your number of rows of data your function is processing
pb <- txtProgressBar(min = start, max = finish, style = 3)
for (i in 1:finish){
i<- progressValue(x)
setTxtProgressBar(pb, i)
if (progressValue(x)/finish == 1) {
close(pb)
}
}
}
finish<- function(x){
return(as.numeric(nrow(x)))
}
progressValue<- function(x){
x=x[[1]][["numberRecordsProcessed"]]
return(as.numeric(x))
}
Now, bring it all together! Repeat loops can be trained to end as long as you know your conditions: "Completed" or "Failed". Repeat "status", which will update the number of records processed, and by doing so this will update your progress bar. When the number of records processed equals the number of rows in your data, the progress bar will quit and so will your repeat loop.
repeat {
status<- lapply(info, FUN = function(x){
rforcecom.checkBatchStatus(session, jobId = x$jobId, batchId = x$id)})
progressBar(status, finish = finish(entry))
if (status[[1]][["state"]]=="Completed") {
break
}
if (status[[1]][["state"]]=="Failed") {
break
}
}

QTableView.setColumnHidden() alternative

Let's suppose I have a table with 30 columns, and want to use QSqlTableModel/QTableView and show only 5 columns. Is there some other way besides 25 times calling a setColumnHidden() function?
model = QSqlTableModel(self)
model.setTable("table")
...
view = QTableView()
view.setModel(model)
...
#insane:
view.setColumnHidden(0, True)
view.setColumnHidden(4, True)
view.setColumnHidden(6, True)
view.setColumnHidden(7, True)
view.setColumnHidden(9, True)
view.setColumnHidden(10, True)
view.setColumnHidden(11, True)
...
view.setColumnHidden(29, True)
And what if DBA add some new columns that I don't want user to see. Making changes to all installed apps to add some new view.setColumnHidden(n, True) rows? Not so practical.
Maybe there is some Qt function like view.setColumnsShown([1,2,3,5,8]) I'm not aware of?
You could define your own setColumnsShown() function:
from sets import Set
def setColumnsShown(view, showcols):
allcols = Set(range(0, view.model().columnCount()))
for col in allcols.difference(showcols):
view.setColumnHidden(col, True)
To handle the case where new columns may be added, you could connect the columnsInserted() signal of QSqlTableModel to a handler function that re-calls setColumnsShown.
I think that there isn't this function, but if you want do this automatically you can write your own function or code snippet, whick will work as you want. Unfortunately I don't familiar with Qt+Python but in C++ it can be done with this code. It is a few loops, so I think that you be able to write same code with Python syntaxis. Also I wrote comments, to show how exactly this code works.
QList<int> list;//create list where we set number of columns to be shown
list<< 1<<2;//write in list numbers of columns
int c = ui->tableView->model()->columnCount();//get count of columns
for (int i = 0; i < c; ++i)
{
ui->tableView->setColumnHidden(i,true);//hide all columns
}
for (int i = 0; i < list.length(); ++i)
{
if(list.at(i) < c)
ui->tableView->setColumnHidden(list.at(i),false);//show columns which we want
}

Resources