ArcMap Field Calculator Program to create Unique ID's - python

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.

Related

Usage of a random function and lists

Here is my code
N=str(input("Enter the catagory: "))
Countries=["canada","albania","cuba"]
if N=="Countries":
y=random.choice(Countries)
if I do something like this the code takes the elements in the countries but when I try to create a function by using it I fail. For example I have many catogories so I dont want to write if function like 10 times for every catagory.Hence I tried to write it like this
N=str(input("Enter the catagory: "))
Countries=["canada","albania","cuba"]
def cata(N):
y=random.choice(N)
z=len(str(y))
return (z,y)
but at this time when I type countries I only get the letters of the countries so the code refers to the word "countries" but the list name.And I need the elements of the countries list. I am not sure how to fix it
thanks.
Well after the function ends I need both y and z values.
If you have a number of categories, and you need a single function to return a random value from one of those list, depending on the name of the list that you send, I think your best way of doing this is through a dictionary with lists as value. Something like
dic = {
'countries' : ["A","B","C"]
'cities' : ["X","Y","Z"]
}
Now your function can take the name of the list as a parameter, and use it to look up in the dictionary to get the appropriate list, and return a random value from that
N = str(input("Enter the catagory: "))
def cata(n):
y = random.choice(dic[n])
cata(N)
You are confusing a str (a piece of data) with variables and a piece of program code.
If user enters 'Countries' to the input, the string N gets turned into a list ['C', 'o', 'u', ...] inside random.choice(N).
To "fix" your program, you can do it this way:
Countries = ["canada","albania","cuba"]
N = str(input("Enter the catagory: "))
y = random.choice(eval(N))
...
HOWEVER, it is strongly discouraged to apply eval() to a string that user input. Most users can be expected to make mistakes at least some of the time, and a malicious user can abuse it to break your program or even your system.

Python pandas if statement based off of boolean qualifier

I am try to do an IF statement where it keeps my currency pairs in alphabetic ordering (i.e. USD/EUR would flip to EUR/USD because E alphabetically comes before U, however CHF/JPY would stay the same because C comes alphabetically before J.) Initially I was going to write code specific to that, but realized there were other fields I'd need to flip (mainly changing a sign for positive to negative or vice versa.)
So what I did was write a function to create a new column and make a boolean identifier as to whether or not the field needs action (True) or not (False).
def flipFx(ccypair):
first = ccypair[:3]
last = ccypair[-3:]
if(first > last):
return True
else:
return False
brsPosFwd['Flip?'] = brsPosFwd['Currency Pair'].apply(flipFx)
This works great and does what I want it to.
Then I try and write an IF statement to use that field to create two new columns:
if brsPosFwd['Flip?'] is True:
brsPosFwd['CurrencyFlip'] = brsPosFwd['Sec Desc'].apply(lambda x:
x.str[-3:]+"/"+x.str[:3])
brsPosFwd['NotionalFlip'] = -brsPosFwd['Current Face']
else:
brsPosFwd['CurrencyFlip'] = brsPosFwd['Sec Desc']
brsPosFwd['NotionalFlip'] = brsPosFwd['Current Face']
However, this is not working properly. It's creating the two new fields, CurrencyFlip and NotionalFlip but treating every record like it is False and just pasting what came before it.
Does anyone have any ideas?
Pandas uses vectorised functions. You are performing operations on entire series objects as if they were single elements.
You can use numpy.where to vectorise your calculations:
import numpy as np
brsPosFwd['CurrencyFlip'] = np.where(brsPosFwd['Flip?'],
brsPosFwd['Sec Desc'].str[-3:]+'/'+brsPosFwd['Sec Desc'].str[:3]),
brsPosFwd['Sec Desc'])
brsPosFwd['NotionalFlip'] = np.where(brsPosFwd['Flip?'],
-brsPosFwd['Current Face'],
brsPosFwd['Current Face'])
Note also that pd.Series.apply should be used as a last resort; since it is a thinly veiled inefficient loop. Here you can simply use the .str accessor.

TypeError: tuple indices must be integers or slices, not str

I need to make a function that updates tuples in a list of tuples. The tuples contain transactions, which are characterised by ammount, day, and type. I made this function that should completely replace a tuple with a new one, but when I try to print the updated list of tuples I get the error:
TypeError: tuple indices must be integers or slices, not str
The code:
def addtransaction(transactions, ammount, day, type):
newtransactions = {
"Ammount": ammount,
"Day": day,
"Type": type
}
transactions.append(newtransaction)
def show_addtransaction(transactions):
Ammount = float(input("Ammount: "))
Day = input("Day: ")
Type = input("Type: ")
addtransaction(transactions, ammount, day, type)
def show_all_transaction(transactions):
print()
for i, transaction in enumerate(transactions):
print("{0}. Transaction with the ammount of {1} on day {2} of type: {3}".format(
i + 1,
transaction['Ammount'], ; Here is where the error occurs.
transaction['Day'],
transaction['Type']))
def update_transaction(transactions): ; "transactions" is the list of tuples
x = input("Pick a transaction by index:")
a = float(input("Choose a new ammount:"))
b = input("Choose a new day:")
c = input("Choose a new type:")
i = x
transactions[int(i)] = (a, b, c)
addtransaction(transactions, 1, 2, service)
show_all_transaction(transactions)
update_transaction(transactions)
show_all_transaction(transactions)
A tuple is basically only a list, with the difference that in a tuple you cannot overwrite a value in it without creating a new tuple.
This means you can only access each value by an index starting at 0, like transactions[0][0].
But as it appears you should actually use a dict in the first place. So you need to rewrite update_transaction to actually create a dict similar to how addtransaction works. But instead of adding the new transaction to the end you just need to overwrite the transaction at the given index.
This is what update_transaction already does, but it overwrites it with a tuple and not a dict. And when you print it out, it cannot handle that and causes this error.
Original answer (Before I knew the other functions)
If you want to use strings as indexes you need to use a dict. Alternatively you can use namedtuple which are like tuples but it also has an attribute for each value with the name you defined before. So in your case it would be something like:
from collections import namedtuple
Transaction = namedtuple("Transaction", "amount day type")
The names given by the string used to create Transaction and separated by spaces or commas (or both). You can create transactions by simply calling that new object. And accessing either by index or name.
new_transaction = Transaction(the_amount, the_day, the_type)
print(new_transaction[0])
print(new_transaction.amount)
Please note that doing new_transaction["amount"] will still not work.
This is not a generic answer, I'll just mention it if someone bumps into the same problem.
As stated before, tuples are addressed by integer e.g. my_tuple[int] or slice my_tuple[int1:int2].
I ran into trouble when I ported code from Python2 to Python3. The original code used somthing like my_tuple[int1/int2], this worked in Python2 since division int/int results in int.
in Python3 int/int results in a floating point number.
I had to fix the code to my_tuple[int1//int2] to get the python2 behavior.

How to transform a column field into a list

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

Python data structure recommendation?

I currently have a structure that is a dict: each value is a list that contains numeric values. Each of these numeric lists contain what (to borrow a SQL idiom) you could call a primary key containing the first three values which are: a year, a player identifier, and a team identifier. This is the key for the dict.
So you can get a unique row by passing the a value in for the year, player ID, and team ID like so:
statline = stats[(2001, 'SEA', 'suzukic01')]
Which yields something like
[305, 20, 444, 330, 45]
I'd like to alter this data structure to be quickly summed by either of these three keys: so you could easily slice the totals for a given index in the numeric lists by passing in ONE of year, player ID, and team ID, and then the index. I want to be able to do something like
hr_total = stats[year=2001, idx=3]
Where that idx of 3 corresponds to the third column in the numeric list(s) that would be retrieved.
Any ideas?
Read up on Data Warehousing. Any book.
Read up on Star Schema Design. Any book. Seriously.
You have several dimensions: Year, Player, Team.
You have one fact: score
You want to have a structure like this.
You then want to create a set of dimension indexes like this.
years = collections.defaultdict( list )
players = collections.defaultdict( list )
teams = collections.defaultdict( list )
Your fact table can be this a collections.namedtuple. You can use something like this.
class ScoreFact( object ):
def __init__( self, year, player, team, score ):
self.year= year
self.player= player
self.team= team
self.score= score
years[self.year].append( self )
players[self.player].append( self )
teams[self.team].append( self )
Now you can find all items in a given dimension value. It's a simple list attached to a dimension value.
years['2001'] are all scores for the given year.
players['SEA'] are all scores for the given player.
etc. You can simply use sum() to add them up. A multi-dimensional query is something like this.
[ x for x in players['SEA'] if x.year == '2001' ]
Put your data into SQLite, and use its relational engine to do the work. You can create an in-memory database and not even have to touch the disk.
The syntax stats[year=2001, idx=3] is invalid Python and there is no way you can make it work with those square brackets and "keyword arguments"; you'll need to have a function or method call in order to accept keyword arguments.
So, say we make it a function, to be called like wells(stats, year=2001, idx=3). I imagine the idx argument is mandatory (which is very peculiar given the call, but you give no indication of what could possibly mean to omit idx) and exactly one of year, playerid, and teamid must be there.
With your current data structure, wells can already be implemented:
def wells(stats, year=None, playerid=None, teamid=None, idx=None):
if idx is None: raise ValueError('idx must be specified')
specifiers = [(i, x) for x in enumerate((year, playerid, teamid)) if x is not None]
if len(specifiers) != 2:
raise ValueError('Exactly one of year, playerid, teamid, must be given')
ikey, keyv = specifiers[0]
return sum(v[idx] for k, v in stats.iteritems() if k[ikey]==keyv)
of course, this is O(N) in the size of stats -- it must examine every entry in it. Please measure correctness and performance with this simple implementation as a baseline. An alternative solutions (much speedier in use, but requiring much time for preparation) is to put three dicts of lists (one each for year, playerid, teamid) to the side of stats, each entry indicating (or copying, but I think indicating by full key may suffice) all entries of stats that match that that ikey / keyv pair. But it's not clear at this time whether this implementation may not be premature, so please try first with the simple-minded idea!-)
def getSum(d, year, idx):
sum = 0
for key in d.keys():
if key[0] == year:
sum += d[key][idx]
return sum
This should get you started. I have made the assumption in this code, that ONLY year will be asked for, but it should be easy enough for you to manipulate this to check for other parameters as well
Cheers

Categories

Resources