How to transform a column field into a list - python

I'm trying to make a simple field calculation with ArcGIS 9.3:
New field = Old field - Old field(first value)
which in Calculate Field with python code should be
Expression: !Old field! - first
Code Block: list= [AngOriz] first = list[0]
The error is
ERROR 000539: Runtime error : name 'AngOriz' is not defined Failed to execute (Calculate Field).
How could I tranform a column field into a list?
I've tried this way
Expression:
makeCalc( !AngOriz!, !AngOriz!)
Code Block:
def makeCalc(x, y):
first_value = y.split(' , ')[0]
return x-first_value
but still I get:
ERROR 000539: Error running expression: makeCalc( 43.01841, 43.01841) : 'float' object has no attribute 'split' Failed to execute (Calculate Field).
I need to calculate the difference between a value of a column and the first value of the same column (values are floating points).
The purpose is to calculate the displacement of different survey points on a landslide.

I'm not getting the whole picture, what do you mean by Old field(first value)?? is Old field a text field with multiple values on it separated by commas or something like that? and if so, how come you use Old field before that?
But anyway, the error you're getting is because on the code block you haven't defined AngOriz... the best way to do what you're trying to do is inside a function, which gets the necessary values to work with as parameters, and returns the value to the field, here is an example:
on the Expression field:
my_function(!Old field!, !some_other_field!)
and on the codeblock:
def my_function(old_field, other_field):
# supposing other_field is a list of int-values in a string = "1, 23, 5"
first_value = int(other_field.split(', ')[0])
return old_field - first_value

Sorry for taking so long to answer. Had a busy week :-) Ok, I thought you meant a string field, not a float field, so forget about the split-stuff. I'm leaving the answer above, as it may help someone else looking for how to transform a (string) field into a list and use its values for a field calculation.
If I'm getting this right now, you mean the value of the first row of column AngOriz, right? So if this value doesn't change, then just use it "hard coded" on the function like this:
on the Expression field:
my_function(!AngOriz!)
and on the codeblock:
def my_function(old_field):
return old_field - 34.123 # just type the value of the first row here
If you don't want to "hard code" the value of the first row, then you would have to retrieve it somehow, but I'm not sure how you could do this on the field calculator. Probably you would have to solve this in a completely different way. With a script in arcpy or so.
Update:
There is also the possibility of using global variables inside the field calculator - this means variables that are persistent between function calls. Because the function gets called by the field calculator for each row, and normally all variables inside a function "die" between calls, it is impossible to "remember" something on local variables. But you could use a global variable to save the first row's value and use it on the other following calls. I haven't tried it, but maybe something like this would work:
firstValue = None
def my_function(old_field):
global firstValue
if firstValue == None: # this will be true only if this is the first call/row ...
firstValue = old_field # ... save the value on the global variable
return old_field - firstValue

Related

Assigning a value from a dataframe to a dynamically created variable

I have a simple pandas dataframe with a column 'mycol' and has 5 rows in it and I'm trying to create 5 new variables for every row value, something like below:
newcol_1=df['mycol'][0]
newcol_2=df['mycol'][1]
newcol_3=df['mycol'][2]
newcol_4=df['mycol'][3]
newcol_5=df['mycol'][4]
I don't want to hard code as above and I'm using the below "for loop" but it keeps throwing 'can't assign to operator'. I know that assignment values should be on the right and variable on the left but not sure how do I use for loop to dynamically create these 5 variables.
for i in 0, df.shape[0]-1:
#"newcol_"+str(i+1) =df['mycol'][i] # this isn't working
newcol_+str(i+1) =df['mycol'][i] # this also isn't working
Appreciate if anyone can help with this...Thanks!
Preferred option: modify globals()
# Create variables dynamically
for i, value in enumerate(df["VALUE"].values):
globals()[f"newcol_{i+1}"] = value
# Test that newcol_3 exists, for instance
print(newcol_3) # Works fine
print(newcol_3 == df['mycol'][2]) # True
Alternate option: use exec()
Though one should do so with caution
# Create a new dict with desired key/value pairs
newcols = {f"newcol_{i+1}": value for i, value in enumerate(df["VALUE"].values)}
# Create new variables dynamically from dict
for name, value in newcols.items():
exec(f"{name}= {value}")
# Previous tests work just as fine

issue with for loop in python only gets the last item

I'm a beginner in python, currently I'm trying to automate filling website field using selenium.
I'm trying to iterate over nested lists using for loop but always get only the last element. Any suggestions why?
fields = [['a','b','c'],['x','y','z']]
for i in range(len(fields)):
driver.find_element_by_xpath("element").send_keys(fields[i][0],fields[i[1],fields[i][2])
driver.find_element_by_xpath("element_save").click()
#then loop and iterate through 2nd nested list
# OUTPUT = x,y,z
I expect to iterate starting with index 0 to the end of the list.
You don't need range(len(list_)) for iterating over indeces only.
Usual for will do. You can also unpack list with *:
fields = [['a','b','c'],['x','y','z']]
len_ = len(fields)
for i in range(len_):
driver.find_element_by_xpath("element").send_keys(*fields[i])
You could also iterate trhrough the values of the fields itself:
fields = [['a','b','c'],['x','y','z']]
for field in fields:
driver.find_element_by_xpath("element").send_keys(*field)
Firstly there is a bug in your program as you have written it:
fields = [['a','b','c'],['x','y','z']]
for i, v in enumerate(fields):
driver.find_element_by_xpath("element").send_keys(fields[i][0],fields[i[1],fields[i][2])
^ # No closing ]
Secondly there is a term that Python developers like to throw around: Pythonic Code.
We like to write short concise code that favors readability over squeezing every last inch of performance.
Referring to this you should change your code as it is unnecessarily cluttered and you are not even utilizing the value element of enumerate. I would recommend the following:
fields = [['a','b','c'],['x','y','z']]
for field in fields:
name, age, height = field # Replace this line with whatever the fields represent
driver.find_element_by_xpath("element").send_keys(name, age, height)
This code is short, concise, and above all extremely readable to someone else.
Note: Replace the name, age, height with whatever they represent in your program.
If in fact this didn't solve your problem, your problem may not be with python but with selenium itself and that is out of the scope of this question. You can test this with simply printing the values before feeding it to the selenium function like this:
fields = [['a','b','c'],['x','y','z']]
for field in fields:
name, age, height = field # Replace this line with whatever the fields represent
print(name, age, height)
driver.find_element_by_xpath("element").send_keys(name, age, height)
Hope this helps.

Python - How to pull an attribute out of list of custom objects and convert it to a float?

I am a novice Python user so I hope I haven't missed something basic but I feel I've done my due diligence in trying to research this problem on my own so here goes.
In brief, I am writing a program that will analyze sports statistics and ultimately generate a rating for the strength of each team based on the chosen statistics.
I can successfully read in simple csv files and I'm reasonably happy with the custom object class I have created to store the statistics as attributes for each team but I am running in to an issue when I go to calculate the rating. Essentially, I need to sort all the teams by each statistic I am interested in, rank the teams by this statistic and assign a point value for the rank of each one. This will produce a cumulative rating score based on the rank for each statistic. However, I'm having some issues in getting the statistic value as a float which I think I need to do in order to sort properly.
Here's the code I've tried:
I've created a team object as seen below. This version is stripped down of most of the attributes for ease of reading but the additional attributes are all very similar.
class team(object):
def__init__(self,teamName="",passOffYc=0, passOffAya=0):
self.teamName = teamName
self.passOffYc = passOffYc
self.passOffAya = passOffAya
self.score = 0
These objects are constructed from a csv file that has a header with the statistical categories and each row represents a team and its stats. I am reading in the file using csv.DictReader like this:
league= []
with open(passoffense) as csvfile:
statreader = csv.DictReader(csvfile, delimiter=',')
for row in statreader:
newTeam = team(row["Tm"],row["Y/C"],row["AY/A"])
print(newTeam, "added")
league.append(newTeam)
At this point I think I have a list called league that contains a team object for each row in the csv file and the teamname, passOffYc, and passOffAya attributes have taken the values for the row elements Tm, Y/C, and AY/A. These are the team name, Yards per Catch, and Average Yards per Attempt so the second two are all decimal numbers.
To try to create the score for each team object, I'd like to sort the league list first by passOffYc, determine the rank of each team, and then repeat for passOffAya and so on for all the attributes in the full version of the program.
I've attempted two different implementations of this trying to understand attrgetter or lambda functions.
My attrgetter thoughts are something like this:
sortedTeams = sorted(league, key = attrgetter("passOffYc"))
My understanding is that this would sort the list called league according to the attribute passOffYc but the issue I'm encountering is that the sort is not producing the expected output.
If passOffYc was 19, 23, 14, 7, and 9, I am expecting the sort to result in 7, 9, 14, 19, 23. However it will end up sorting as 14, 19, 23, 7, 9. My research has led me to believe this is because the values are strings and not integers (or more accurately floats as some values have decimals) Not quite sure how to fix this I tried:
sortedTeams = sorted(league, key = float(attrgetter("passOffYc"))
But this gives me the error:
TypeError: float() argument must be a string or a number, not 'operator.attrgetter'
So apparently it isn't a string and instead is an operator.attrgetter object. I haven't been able to figure out how to get the key for the sorted function to the float type so I also tried using lambda functions I read about while searching:
sortedTeams = sorted(league, key = lambda team: float(team.passOffYc))
This seems very close to what I'd like to happen as it does sort properly but then I run in to a scaling problem. Since I have more than 20 attributes to sort by in the full version of my program I'd like to avoid needing to type the above statement 20 times to accommodate each attribute.
I thought of trying to define a function something to the effect of:
def score(stat):
sortedTeams = sorted(league, key = lambda team: float(team.stat))
I thought this would allow me to pass in to the lambda function which stat I want to sort by but I then get the error:
AttributeError: 'team' object has no attribute 'stat'
I think this is because it may not be possible to pass an argument to a lambda function or that I'm not understanding something because I also tried:
sortedTeams = sorted(league, key = lambda team, stat=stat: float(team.stat))
Which resulted in the same error. Whew! If you're still reading this thank you for wading through my essay. Any ideas how I can solve this?
Once I get the sorting to work properly and can scale it I intend to enumerate the sorted lists to obtain the ranks and I'm beginning to think about strategies to address ties. Thank you again for any help!
You just need to create the original team entries with float contents:
newTeam = team(row["Tm"],float(row["Y/C"]),float(row["AY/A"]))
If instead you want to pursue the lambda approach you can use:
sortedTeams = sorted(league, key = lambda team: float(attrgetter("passOffYc")(team)))
This could similarly be used in your function score function. What you were missing is that attrgetter returns a function. You can then call that function by passing it an argument (in this case team). Then that result can be passed to float. In that function you could use:
lambda team: float(attrgetter(stat)(team))
As I understand, you whant to pass a string name of desired field into function. If that is right, then instead of:
def score(stat):
sortedTeams = sorted(league, key = lambda team: float(team.stat))
Try this:
def score(stat):
sortedTeams = sorted(league, key = lambda team: float(getattr(team, stat)))
Some explanation. team.stat - accessing an attribute with name "stat" for object team. getattr(team, stat) - accessing an attribute with name given in the stat variable for object team.

ArcMap Field Calculator Program to create Unique ID's

I'm using the Field Calculator in ArcMap and
I need to create a unique ID for every storm drain in my county.
An ID Should look something like this: 16-I-003
The first number is the municipal number which is in the column/field titled "Munic"
The letter is using the letter in the column/field titled "Point"
The last number is simply just 1 to however many drains there are in a municipality.
So far I have:
rec=0
def autoIncrement()
pStart=1
pInterval=1
if(rec==0):
rec=pStart
else:
rec=rec+pInterval
return "16-I-" '{0:03}'.format(rec)
So you can see that I have manually been typing in the municipal number, the letter, and the hyphens. But I would like to use the fields: Munic and Point so I don't have to manually type them in each time it changes.
I'm a beginner when it comes to python and ArcMap, so please dumb things down a little.
I'm not familiar with the ArcMap, so can't directly help you, but you might just change your function to a generator as such:
def StormDrainIDGenerator():
rec = 0
while (rec < 99):
rec += 1
yield "16-I-" '{0:03}'.format(rec)
If you are ok with that, then parameterize the generator to accept the Munic and Point values and use them in your formatting string. You probably should also parameterize the ending value as well.
Use of a generator will allow you to drop it into any later expression that accepts an iterable, so you could create a list of such simply by saying list(StormDrainIDGenerator()).
Is your question on how to get Munic and Point values into the string ID? using .format()?
I think you can use following code to do that.
def autoIncrement(a,b):
global rec
pStart=1
pInterval=1
if(rec==0):
rec=pStart
else:
rec=rec+pInterval
r = "{1}-{2}-{0:03}".format(a,b,rec)
return r
and call
autoIncrement( !Munic! , !Point! )
The r = "{1}-{2}-{0:03}".format(a,b,rec) just replaces the {}s with values of variables a,b which are actually the values of Munic and Point passed to the function.

Append all fields returned as attributes of a pyodbc cursor to a user-defined data type

I have code along these lines:
classinstance.col1 = queryresult.col1
classinstance.col2 = queryresult.col2
classinstance.col3 = queryresult.col3
classinstance.col4 = queryresult.col4
Which adds variables to the classinstance and assigns the values of the queryresult column with the same name as the variable.
I am hoping to make my code a little more flexible, and not need to identify the columns by name. To this end, I was wondering if there was some way to do a loop over all the columns, rather than handle each one individually. Something like this (This is psuedocode rather than actual code, since I'm not sure what it should actually look like):
for each var in vars(queryresult):
classinstance.(var.name) = var.value
Is this possible? What does it require? Is there some fundamental misunderstanding on my part?
I'm assuming there's only one row in the result for the following example (built with help from comments here). The key component here is zip(row.cursor_description, row) used to get column names from pyodbc.Row object.
# convert row to an object, assuming row variable contains query result
rowdict = { key[0]:value for (key, value) in zip(row.cursor_description, row) }
# loop through keys (equivalent to column names) and set class instance values
# assumes existing instance of class is variable classinstance
for column in rowdict.keys():
classinstance[column] = rowdict[column]

Categories

Resources