Python for ArcGIS - Calculate field with a text field as parameter - python

I am trying to create and calculate a field called Score in an attribute table. Score would depend on two other fields, Width and Category. Width contains integer values, Category contains text. The Score field should assign a number of points based on the values of these two fields.
I was able to write a Python script assign values to SCORE based on the value of Width. However, once I tried to add Category as a parameter, I kept getting error messages saying that the parameters are not valid.
The code below worked (Width only):
expression = "getClass(float(!Width!))"
codeblock = """def getClass(wd):
if wd<=450:
return 1
elif wd>450 and wd<900:
return 2
else:
return 3"""
arcpy.AddField_management("featurelayer", "Score", "SHORT")
This code includes Category as a parameter and does not work:
expression = "getClass(!Width!,!Category!)"
codeblock = """def getClass(wd,cat):
if wd<=35:
return 1
elif wd<90 and cat=='RED':
return 2
else:
return 3"""
arcpy.AddField_management("featurelayer", "Score", "SHORT")
Thank you!
***I did try converting the two parameters in the second code - I tried float() for both, as well as str() for Category since it is a text field. Neither one worked, and the error messages were not very specific: "failed to execute getClass()"
***The Category field is completely populated (either 'RED' or 'BLUE')

Using the expression and codeblock method is necessary within ArcMap's Field Calculator, but not when writing a Python script.
Try an UpdateCursor instead. It is easier to read, and tends to be easier to debug as a result.
arcpy.AddField_management("featurelayer", "Score", "SHORT")
updateFields = ["Width", "Category", "Score"]
with arcpy.da.UpdateCursor("featurelayer", updateFields) as cursor:
for row in cursor:
if row[0] <= 35:
row[2] = 1
elif row[0] < 90 and row[1] == 'RED':
row[2] = 2
else:
row[2] = 3
cursor.updateRow(row)

Related

Unable to change value of dataframe at specific location

So I'm trying to go through my dataframe in pandas and if the value of two columns is equal to something, then I change a value in that location, here is a simplified version of the loop I've been using (I changed the values of the if/else function because the original used regex and stuff and was quite complicated):
pro_cr = ["IgA", "IgG", "IgE"] # CR's considered productive
rows_changed = 0
prod_to_unk = 0
unk_to_prod = 0
changed_ids = []
for index in df_sample.index:
if num=1 and color="red":
pass
elif num=2 and color="blue":
prod_to_unk += 1
changed_ids.append(df_sample.loc[index, "Sequence ID"])
df_sample.at[index, "Functionality"] = "unknown"
rows_changed += 1
elif num=3 and color="green":
unk_to_prod += 1
changed_ids.append(df_sample.loc[index, "Sequence ID"])
df_sample.at[index, "Functionality"] = "productive"
rows_changed += 1
else:
pass
print("Number of productive columns changed to unknown: {}".format(prod_to_unk))
print("Number of unknown columns changed to productive: {}".format(unk_to_prod))
print("Total number of rows changed: {}".format(rows_changed))
So the main problem is the changing code:
df_sample.at[index, "Functionality"] = "unknown" # or productive
If I run this code without these lines of code, it works properly, it finds all the correct locations, tells me how many were changed and what their ID's are, which I can use to validate with the CSV file.
If I use df_sample["Functionality"][index] = "unknown" # or productive the code runs, but checking the rows that have been changed shows that they were not changed at all.
When I use df.at[row, column] = value I get "AttributeError: 'BlockManager' object has no attribute 'T'"
I have no idea why this is showing up. There are no duplicate columns. Hope this was clear (if not let me know and I'll try to clarify it). Thanks!
To be honest, I've never used df.at - but try using df.loc instead:
df_sample.loc[index, "Functionality"] = "unknown"
You can also iat.
Example: df.iat[iTH row, jTH column]

Function takes exactly 3 arguments (1 given)? Help formatting print statement

Here are my questions:
Create a function called "numSchools" that counts the schools of a specific type. The function should have three input parameters, (1) a string for the workspace, (2) a string for the shapefile name, and (3) a string for the facility type (e.g. "HIGH SCHOOL"), and one output parameter, (1) an integer for the number of schools of that facility type in the shapefile.
import arcpy
shapefile = "Schools.shp"
work = r"c:\Scripts\Lab 6 Data"
sTyp = "HIGH SCHOOL"
def numSchools(work, shapefile, sTyp):
whereClause = "\"FACILITY\" = 'HIGH SCHOOL' " # where clause for high schools
field = ['FACILITY']
searchCurs = arcpy.SearchCursor(shapefile, field, whereClause)
row = searchCurs.next()
for row in searchCurs:
# using getValue() to get the name of the high school
value = row.getValue("NAME")
high_schools = [row[0] for row in arcpy.SearchCursor(shapefile, field, whereClause)]
count = arcpy.GetCount_management(high_schools)
return count
numSchools(work, shapefile, sTyp)
print ("There are a total of: "),count
So this is my code that runs perfectly, but it is accomplished by scripting. I need to wrap it into a python function. (MY WEAKNESS). It seems there are some problems with the last line of my code. `
I am not quite sure how to format this last line of code to read
(there are a total of 29 high schools) while including necessary arguments.
You need to explicitly pass the arguments.
count = numSchools(work, shapefile, sTyp)
print("There are a total of: ", count)

Python syntax error with arcpy UpdateCursor

I am new to python and would like to have a script that looks at a feature class and compares the values in two text fields and then populates a third field with a Y or N depending on if the values are the same or not. I think I need to use an UpdateCursor with an if statement. I have tried the following but I get a syntax error when I try to run it. I am using ArcGIS 10.1 and know that the daCursor is better but I am just trying to wrap my head around cursors and thought I would try and keep it simple for now.
#import system modules
import arcpy
from arcpy import env
import os
import sys
#set environment settings
working_fc = sys.argv[1]
working_gdb = os.path.split(working_fc)[0]
#use an update cursor to populate the field BEC_UPDATED based on the result of a query
#query = ("SELECT * FROM working_fc" "WHERE [BEC_LABEL] = [BEC_V9]")
#if the query is true, then BEC_UPDATED should be popluated with "N"
#if the query is false, then BEC_UPDATED should be populated with "Y"
rows = arcpy.UpdateCursor (working_fc)
for row in rows:
if row.getValue("BEC_LABEL") == row.getValue("BEC_V9")
row.BEC_UPDATED = "N"
else
row.BEC_UPDATED = "Y"
rows.updateRow(row)
print "BEC_UPDATED field populated"
Your syntax error is caused by indentation and missing colons. Python is picky about that, so always check that when you're getting a syntax error.
rows = arcpy.UpdateCursor(working_fc)
for row in rows:
if row.getValue("BEC_LABEL") == row.getValue("BEC_V9"):
row.BEC_UPDATED = "N"
else:
row.BEC_UPDATED = "Y"
rows.updateRow(row)
Changing this to the da.UpdateCursor syntax is essentially the same, but requires you to specify the attributes you are interested in up front. It's worth practicing, because once you get into more complex scripts it will become easier :)
fieldList = ["BEC_LABEL", "BEC_V9", "BEC_UPDATED"]
with arcpy.da.UpdateCursor(working_fc, fieldList) as cursor:
for row in cursor:
if row[0] == row[1]:
row[2] = "N"
else:
row[2] = "Y"
cursor.updateRow(row)
You've forgotten the colons and indentation in your 'if' block:
rows = arcpy.UpdateCursor (working_fc)
for row in rows:
if row.getValue("BEC_LABEL") == row.getValue("BEC_V9"):
row.BEC_UPDATED = "N"
else:
row.BEC_UPDATED = "Y"
rows.updateRow(row)
print "BEC_UPDATED field populated"

How do I store a string (name) against 3 corresponding integer values (scores)

I'm trying to write a program that will store the results from a quiz against a player's name. I need to always have a record of the past 3 attempts against the users name. I need to be able to store these results for an indeterminate number of players across 3 class groups (hence the 3 arrays). So far i've got this but am getting pretty stuck now.
I've got 3 arrays with 3 fields. The first field is intended for the name and the following 3 to store the score attempts.
cla_1results = ([],[],[],[])
cla_2results = ([],[],[],[])
cla_3results = ([],[],[],[])
file = open("results.txt"")
if statement determines which array to store the results data in depending on the class code
if class_code == "CL1":
cla_1results[0].append(full_name)
cla_1results[1].append(total)
file.write([cla_1results])
elif class_code == "CL2":
cla_2results[0].append(full_name)
cla_2results[1].append(total)
file.write([cla_2results])
elif class_code == "CL3":
cla_3results[0].append(full_name)
cla_3results[1].append(total)
file.write([cla_3results])
As far as the structure of storing the scores goes, try using something that looks more like this
cla_1results={}
cla_2results={}
cla_3results={}
my_file=open("results.txt"."r+")
if class_code=="CL1":
if full_name in cla_1results:
cla_1results.append(total)
else:
cla_1results[full_name]=[total]
my_file.write(cla_1results[full_name])
elif class_code=="CL2":
if full_name in cla_2results:
cla_2results.append(total)
else:
cla_2results[full_name]=[total]
my_file.write(cla_2results[full_name])
elif class_code=="CL3":
if full_name in cla_3results:
cla_3results.append(total)
else:
cla_3results[full_name]=[total]
my_file.write(cla_3results[full_name])
Also, if you wanted to remove the oldest core if the person had more than 3, you could add in a bit that has cla_1results[full_name][0].remove. Hopefully this helps.

How to call in a specifc csv field value in python

I am so new to python (a week in) so I hope I ask this question properly.
I have imported a grade sheet in csv format into python 2.7. The first column is the name of the student and the column titles are the name of the assignments. So the data looks something like this:
Name Test1 Test2 Test3
Robin 89 78 100
...
Rick 72 100 98
I want to be able to do (or have someone else do) 3 things just by typing in the name of the person and the assignment.
1. Get the score for that person for that assignment
2. Get the average score for that assignment
3. Get that persons average score
But for some reason I get lost at figuring how to get python to recognize the field I am trying to call in. So far this is what I have (so far the only part that works is calling in file):
data = csv.DictReader(open("C:\file.csv"))
for row in data:
print row
def grade()
student= input ("Enter a student name: ")
assignment= input("Enter a assignment: ")
for row in data:
task_grade= data.get(int(row["student"], int(row["assignment"])) # specific grade
task_total= sum(int(row['assignment'])) #assignment total
student_total= #student assignments total-- no clue how to do this
task_average= task_total/11
average_score= student_total/9
You can access the individual "columns" of your csv this way:
import csv
def parse_csv():
csv_file = open('data.csv', 'r')
r = csv.reader(csv_file)
grade_averages = {}
for row in r:
if row[0].startswith('Name'):
continue
#print "Student: ", row[0]
grades = []
for column in row[1:]:
#print "Grade: ", column
grades.append(int(column.strip()))
grade_total = 0
for i in grades:
grade_total += i
grade_averages[row[0]] = grade_total / len(grades)
#print "grade_averages: ", grade_averages
return grade_averages
def get_grade(student_name):
grade_averages = parse_csv()
return grade_averages[student_name]
print "Rick: ", get_grade('Rick')
print "Robin: ", get_grade('Robin')
What you are trying to do is not meant for Python because you have keys and values. However...
If you know that your columns are always the same, no need to use keywords, you can use positions:
Here is the easy, inefficient* way to do 1 and 3:
students_name = ...
number = ...
for line in open("C:\file.csv")).readlines()
items = line.split()
num_assignments = len(items)-1
name = items[0]
if name = students_name:
print("assignment score: {0}".format(items[number]))
asum = 0
for k in range(0,num_assignments):
asum+= items[k+1]
print("their average: {0}".format(asum / num_assignments)
To do 2, you should precompute the averages and return them beucase the averages for each assignment is the same for each user query.
I say easy *innefficnet because you search the text file for each user query each time a name is entered. To do it properly, you should probably build a dictionary of all names and their information. But that solution is more complicated, and you are only a week in! Moreover, its longer and you should give it a try. Look up dict.
I believe the reason you are not seeing the field the second time around is because the iterator returned by csv.DictReader() is a one-time iterator. That is to say, once you've reached the last row of the csv file, it will not reset to the first position.
So, by doing this:
data = csv.DictReader(open("C:\file.csv"))
for row in data:
print row
You are running it out. Try commenting those lines and see if that helps.

Categories

Resources