<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Ditch the Cursor
Editing Feature Classes with Spatialy-Enabled DataFrames
ArcPy Is Great, But...
Problem one
Step1:  We can access individual rows and columns using .loc (with index labels) or .iloc (with indices)
python
medians_df.loc[row labels, column labels]
medians_df.iloc[row indices, column indices]
Step2:  We can also get just a few columns from all rows
Step3:  Extending pandas Spatially
The ArcGIS API for Python provides Spatially Enabled DataFrames, which include geometry information.
Step4:  pandas lets you work on rows that meet a certain condition
Step5:  You can easily add new columns
Step6:  pandas provides powerful built in grouping and aggregation tools, along with Spatially Enabled DataFrames' geometry operations
Step7:  pandas Solutions to our Arcpy Problems
row[0] Solution
Step9:  Nested Cursors Solution
Step10:  Renaming/Reordering Fields Solution
Step11:  Now that we've got it all looking good, let's reorder the fields and get rid of the ones we don't want
Step12:  Intermediate Feature Classes
<ASSISTANT_TASK:>
Python Code:
import pandas as pd
medians_df = pd.read_csv('assets/median_age.csv')
medians_df.head()
Explanation: Ditch the Cursor
Editing Feature Classes with Spatialy-Enabled DataFrames
ArcPy Is Great, But...
Problem one: row[0]
```python
def update_year_built(layer, year_fields):
    with arcpy.da.UpdateCursor(layer, year_fields) as cursor:
        for row in cursor:
            if row[0] is None or row[0] < 1 or row[0] == '':
                row[0] = f'{row[1]}{row[2]}' 
        cursor.updateRow(row)
```
```python
If new parcels' owner/owner addr changed, or if PID is new, add to appropriate lists
with arcpy.da.SearchCursor(tville_parcels_fc, parcel_check_fields) as new_cursor:
    for row in new_cursor:
        if row[0] in old_parcels:
            old_name = old_parcels[row[0]][0]
            old_addr = old_parcels[row[0]][1]
            if row[1] != old_name or row[2] != old_addr:
                own_addr_changed.append(row[0])
        else:
            new_parcels.append(row[0])
```
Problem 2: Nested cursors to transfer data between feature classes
python
with arcpy.da.SearchCursor(new_data_fc, fields) as new_data_cursor, \
     arcpy.da.InsertCursor(current_data_fc, fields) as current_data_cursor:
    for row in new_data_cursor:
        current_data_cursor.insertRow(row)
        copied_records += 1
Problem 3: Renaming/reordering fields
```python
fieldmappings = arcpy.FieldMappings()
fieldmappings.addTable(energov_parcels_fc)
fieldmappings.addTable(tville_parcels_fc)
fields_list = [
    ('PIN', 'parcel_id'),
    ('own_cityst', 'own_citystate'),
    ('own_zip_fo', 'own_zip_four'),
    ('prop_locat', 'prop_location'),
    ('property_t', 'property_type'),
    ('neighborho', 'neighborhood_code'),
    ('adjusted_p', 'adjusted_prcl_total'),
    #: ...
]
for field_map in fields_list:
    field_to_map_index = fieldmappings.findFieldMapIndex(field_map[0])
    field_to_map = fieldmappings.getFieldMap(field_to_map_index)
    field_to_map.addInputField(tville_parcels_fc, field_map[1])
    fieldmappings.replaceFieldMap(field_to_map_index, field_to_map)
```
Problem 4: Intermediate feature classes
python
ssa_summarized_roads = fr'{output_gdb}\ssa_bike_lanes_roads'
ssa_summarized_paths = fr'{output_gdb}\ssa_bike_lanes_paths'
ssa_summarized_lengths = fr'{output_gdb}b\SmallStatisticalAreas_2018_bike_lane_lengths'
tract_summarized_roads = fr'{output_gdb}\tract_bike_lanes_roads'
tract_summarized_paths = fr'{output_gdb}\tract_bike_lanes_paths'
tract_summarized_lengths = fr'{output_gdb}\census_tracts_2020_bike_lane_lengths'
buffered_tracts = fr'{output_gdb}\census_tracts_2020_buffered_30ft'
buffered_areas = fr'{output_gdb}\small_areas_buffered_200ft'
bike_lanes = fr'{output_gdb}\bike_lanes_20220111'
major_paths = fr'{output_gdb}\major_paths'
Enter the Pandas!
pandas gives you the tools to work with tables of data defined by rows and columns, called a DataFrame
End of explanation
medians_df.loc[[0, 1, 2, 5], 'County']
medians_df.iloc[10:15, :4]
Explanation: We can access individual rows and columns using .loc (with index labels) or .iloc (with indices)
python
medians_df.loc[row labels, column labels]
medians_df.iloc[row indices, column indices]
End of explanation
medians_df[['Median_age', 'Avg_MonthlyIncome']].head()
Explanation: We can also get just a few columns from all rows
End of explanation
from arcgis.features import GeoAccessor, GeoSeriesAccessor
counties_fc_path = r'C:\Users\jdadams\AppData\Roaming\Esri\ArcGISPro\Favorites\opensgid.agrc.utah.gov.sde\opensgid.boundaries.county_boundaries'
counties_df = pd.DataFrame.spatial.from_featureclass(counties_fc_path)
counties_df.head()
Explanation: Extending pandas Spatially
The ArcGIS API for Python provides Spatially Enabled DataFrames, which include geometry information.
End of explanation
counties_df.loc[counties_df['stateplane'] == 'Central', ['name', 'stateplane', 'fips_str']]
Explanation: pandas lets you work on rows that meet a certain condition
End of explanation
counties_df['emperor'] = 'Jake'
counties_df.head()
Explanation: You can easily add new columns
End of explanation
counties_df.groupby('stateplane').count()
counties_df['acres'] = counties_df['SHAPE'].apply(lambda shape: shape.area / 4046.8564)
counties_df.groupby('stateplane')['acres'].sum()
Explanation: pandas provides powerful built in grouping and aggregation tools, along with Spatially Enabled DataFrames' geometry operations
End of explanation
counties_df.loc[(counties_df['pop_lastcensus'] < 100000) & (counties_df['stateplane'] == 'North'), 'emperor'] = 'Erik'
counties_df[['name', 'pop_lastcensus', 'stateplane', 'emperor']].sort_values('name').head()
Explanation: pandas Solutions to our Arcpy Problems
row[0] Solution: Field Names
```python
def update_unit_count(parcels_df):
    Update unit counts in-place for single family, duplex, and tri/quad
Args:
    parcels_df (pd.DataFrame): The evaluated parcel dataset with UNIT_COUNT, HOUSE_CNT, SUBTYPE, and NOTE columns
# fix single family (non-pud)
zero_or_null_unit_counts = (parcels_df['UNIT_COUNT'] == 0) | (parcels_df['UNIT_COUNT'].isna())
parcels_df.loc[(zero_or_null_unit_counts) &amp; (parcels_df['SUBTYPE'] == 'single_family'), 'UNIT_COUNT'] = 1
# fix duplex
parcels_df.loc[(parcels_df['SUBTYPE'] == 'duplex'), 'UNIT_COUNT'] = 2
# fix triplex-quadplex
parcels_df.loc[(parcels_df['UNIT_COUNT'] &lt; parcels_df['HOUSE_CNT']) &amp; (parcels_df['NOTE'] == 'triplex-quadplex'),
               'UNIT_COUNT'] = parcels_df['HOUSE_CNT']
```
Let's make Erik the emperor of the small counties that use State Plane North
End of explanation
census_fc_path = r'C:\Users\jdadams\AppData\Roaming\Esri\ArcGISPro\Favorites\opensgid.agrc.utah.gov.sde\opensgid.demographic.census_counties_2020'
census_df = pd.DataFrame.spatial.from_featureclass(census_fc_path)
counties_with_census_df = counties_df.merge(census_df[['geoid20', 'aland20']], left_on='fips_str', right_on='geoid20')
counties_with_census_df.head()
Explanation: Nested Cursors Solution: Merged DataFrames
```python
def _get_current_attachment_info_by_oid(self, live_data_subset_df):
#: Join live attachment table to feature layer info
live_attachments_df = pd.DataFrame(self.feature_layer.attachments.search())
live_attachments_subset_df = live_attachments_df.reindex(columns=['PARENTOBJECTID', 'NAME', 'ID'])
merged_df = live_data_subset_df.merge(
    live_attachments_subset_df, left_on='OBJECTID', right_on='PARENTOBJECTID', how='left'
)
return merged_df
```
Let's add census data to our counties
End of explanation
renames = {
    'name': 'County Name',
    'pop_lastcensus': 'Last Census Population',
    'emperor': 'Benevolent Dictator for Life',
    'acres': 'Acres',
    'aland20': 'Land Area',
}
counties_with_census_df.rename(columns=renames, inplace=True)
counties_with_census_df.head()
Explanation: Renaming/Reordering Fields Solution: df.rename() and df.reindex()
python
final_parcels_df.rename(
    columns={
        'name': 'CITY',  #: from cities
        'NewSA': 'SUBCOUNTY',  #: From subcounties/regions
        'BUILT_YR': 'APX_BLT_YR',
        'BLDG_SQFT': 'TOT_BD_FT2',
        'TOTAL_MKT_VALUE': 'TOT_VALUE',
        'PARCEL_ACRES': 'ACRES',
    },
    inplace=True
)
```python
final_fields = [
    'SHAPE', 'UNIT_ID', 'TYPE', 'SUBTYPE', 'IS_OUG', 'UNIT_COUNT', 'DUA', 'ACRES', 'TOT_BD_FT2', 'TOT_VALUE',
    'APX_BLT_YR', 'BLT_DECADE', 'CITY', 'COUNTY', 'SUBCOUNTY', 'PARCEL_ID'
]
logging.info('Writing final data out to disk...')
output_df = final_parcels_df.reindex(columns=final_fields)
output_df.spatial.to_featureclass(output_fc, sanitize_columns=False)
```
"Emperor" is too bold; let's use "Benevolent Dictator for Life" instead.
End of explanation
field_order = [
    'County Name',
    'Benevolent Dictator for Life',
    'Acres',
    'Land Area',
    'Last Census Population',
    'SHAPE'
]
final_counties_df = counties_with_census_df.reindex(columns=field_order)
final_counties_df.head()
Explanation: Now that we've got it all looking good, let's reorder the fields and get rid of the ones we don't want
End of explanation
final_counties_df.spatial.to_featureclass(r'C:\gis\Projects\HousingInventory\HousingInventory.gdb\counties_ugic')
Explanation: Intermediate Feature Classes: New DataFrame Variables
With everything we've done, we've not written a single feature class to either disk or in_memory
python
counties_df
counties_with_census_df
final_counties_df
Finally, Write It All To Disk
End of explanation
<END_TASK>