Problem with python script: can only concatenate list (not "str") to list

4511
8
Jump to solution
11-08-2017 04:13 AM
JohannesBierer
Regular Contributor

I've got a problem with the following script:

import arcpy, os

infile = r"C:\temp4\ALK_Merge.shp"
infileL = "infile_Layer"

count = int(arcpy.GetCount_management(infile).getOutput(0))

x = 99
xC = count/x

xC1 = xC + 2

for i in range(1, xC1):
    print i
    
    sPath = infile.split(".shp")
    
    print sPath
    
    xRange = i * 99
    xRange1 = (i*99)-99
    print xRange
    print xRange1

    outFeature = sPath + "_" + i + ".shp"

    arcpy.MakeFeatureLayer_management (infile, infileL)
    arcpy.SelectLayerByAttribute_management (infileL, "NEW_SELECTION", "\"FID\" <= " + xRange + "AND" + "\"FID\" >= "+ xRange1 + "")
    arcpy.CopyFeatures_management(infileL, outFeature)

The problem is in line 25:

outFeature = sPath + "_" + i + ".shp"
TypeError: can only concatenate list (not "str") to list

Any ideas how to solve this?

0 Kudos
1 Solution

Accepted Solutions
XanderBakker
Esri Esteemed Contributor

The method you are using to create the chunks to get exactly 99 features will only work in case there were no deletes in the featureclass.

Take the following example:

def main():
    import arcpy

    fc = r'C:\GeoNet\ArcadeMedellin\ArcadeMedellin.gdb\Solicitudes'
    oids = [r[0] for r in arcpy.da.SearchCursor(fc, ('OID@'))]

    print "FID 99 is at index:", oids.index(99)

    cnt = 0
    for lst in chunks(oids, 99):
        cnt += 1
        print "\nChunk {0} has {1} elements".format(cnt, len(lst))
        print " - OID from {0} to {1}".format(min(lst), max(lst))

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]

if __name__ == '__main__':
    main()

this will yield:

FID 99 is at index: 92

Chunk 1 has 99 elements
 - OID from 1 to 105

Chunk 2 has 99 elements
 - OID from 106 to 211

Chunk 3 has 36 elements
 - OID from 212 to 250

The OID are not continuous, so the chunk function provides a more stable way of creating the appropriate chucks of 99 elements.

View solution in original post

8 Replies
XanderBakker
Esri Esteemed Contributor

When yo do sPath = infile.split(".shp") on line 16 he variable sPath will be a list and can't be concatenated with a string. However there are more things going on in your code. I will post an alternative later.

0 Kudos
JohannesBierer
Regular Contributor

Because I want to tile the "bigger" shape into "shapes" with exactly 99 features and therefore need to give different names for the output feature class.

0 Kudos
XanderBakker
Esri Esteemed Contributor

try this:

def main():
    import arcpy
    import os
    arcpy.env.overwriteOutput = True

    infile = r"C:\temp4\ALK_Merge.shp"
    infileL = "infile_Layer"

    count = int(arcpy.GetCount_management(infile).getOutput(0))
    x = 99
    xC = count/x
    xC1 = xC + 2

    for i in range(1, xC1):
        print i

        # this will cut off the extension .shp
        fc_pathname = os.path.splitext(infile)[0]

        xRange = i * 99
        xRange1 = (i*99)-99
        print xRange
        print xRange1

        # this will create the file path to the output shapefile
        fc_pathname = "{0}_{1}.shp".format(fc_pathname, i)

        arcpy.MakeFeatureLayer_management (infile, infileL)

        # this will define the where clause
        where = "FID >= {0} AND FID <= {1}".format(xRange1, xRange)
        arcpy.SelectLayerByAttribute_management (infileL, "NEW_SELECTION", where)
        arcpy.CopyFeatures_management(infileL, outFeature)


if __name__ == '__main__':
    main()
XanderBakker
Esri Esteemed Contributor

The method you are using to create the chunks to get exactly 99 features will only work in case there were no deletes in the featureclass.

Take the following example:

def main():
    import arcpy

    fc = r'C:\GeoNet\ArcadeMedellin\ArcadeMedellin.gdb\Solicitudes'
    oids = [r[0] for r in arcpy.da.SearchCursor(fc, ('OID@'))]

    print "FID 99 is at index:", oids.index(99)

    cnt = 0
    for lst in chunks(oids, 99):
        cnt += 1
        print "\nChunk {0} has {1} elements".format(cnt, len(lst))
        print " - OID from {0} to {1}".format(min(lst), max(lst))

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]

if __name__ == '__main__':
    main()

this will yield:

FID 99 is at index: 92

Chunk 1 has 99 elements
 - OID from 1 to 105

Chunk 2 has 99 elements
 - OID from 106 to 211

Chunk 3 has 36 elements
 - OID from 212 to 250

The OID are not continuous, so the chunk function provides a more stable way of creating the appropriate chucks of 99 elements.

JoshuaBixby
MVP Esteemed Contributor

Not that it will perform any better, I haven't tested, but native slicing and itertools.izip_longest can also be used:

import arcpy
from itertools import izip_longest

chunk_size = #
fc = #

chunk_start = slice(0, None, chunk_size)
chunk_end = slice(chunk_size - 1, None, chunk_size)
with arcpy.da.SearchCursor(fc, "OID@") as cur:
    oids = [oid for oid, in cur]
oid_chunks = izip_longest(oids[chunk_start],
                          oids[chunk_end],
                          fillvalue=oids[-1])
list(oid_chunks)
JohannesBierer
Regular Contributor

Perfect thank you !

Only a very small thing - it's not

arcpy.CopyFeatures_management(infileL, outFeature)

must be:
arcpy.CopyFeatures_management(infileL,fc_pathname) I think?
XanderBakker
Esri Esteemed Contributor

Very true. Good catch!

0 Kudos
JoeBorgione
MVP Esteemed Contributor

Isn't the value of i numeric as well?  That seems problematic as well.  While solidly in the rookie phase in my Python carreer, could you join spath into a string, and then concatenate it with i after casting i as a string:

spath = ''.join(infile.split(".shp"))
outfeature = spath+'_'+str(i)+'_ '".shp"
‍‍‍‍‍‍‍‍

Untested: 6:00 am at the kitchen table with only one cup of coffee on board....

eta... Never mind:  Looks like Xander slipped in his repsonse while I hacking....

xander_bakker

That should just about do it....