I am new in Python and Openpyxl.
I am trying to copy a range of cells (with their formatting knowing some cells are merged) from a row to an other row on the same worksheet.
Thanks
This is an example to copy cells from one row to another within the same worksheet.
This example takes a range of cells on row 2 from column A to column column J and duplicates to 'row_offset' rows down, in this case row 12. The duplication includes cell style/formatting and merged cells.
from openpyxl.utils import rows_from_range
from openpyxl import load_workbook
from openpyxl.worksheet.cell_range import CellRange
from copy import copy
def copy_range(range_str, sheet, offset):
""" Copy cell values and style to the new row using offset"""
for row in rows_from_range(range_str):
for cell in row:
if sheet[cell].value is not None: # Don't copy other cells in merged unit
dst_cell = sheet[cell].offset(row=offset, column=0)
src_cell = sheet[cell]
### Copy Cell value
dst_cell.value = src_cell.value
### Copy Cell Styles
dst_cell.font = copy(src_cell.font)
dst_cell.alignment = copy(src_cell.alignment)
dst_cell.border = copy(src_cell.border)
dst_cell.fill = copy(src_cell.fill)
dst_cell.number_format = src_cell.number_format
def get_merge_list(r_range, r_offset):
""" Create a list of new cell merges from the existing row"""
area = CellRange(r_range) # Range to check for merged cells
mlist = [] # List of merged cells on existing row offset to the new row
for mc in ws.merged_cells:
if mc.coord not in area:
continue
cr = CellRange(mc.coord)
cr.shift(row_shift=r_offset)
mlist.append(cr.coord)
return mlist
wb = load_workbook("foo.xlsx")
ws = wb['Sheet1']
row_range = 'A2:J2' # Row range to be copied
row_offset = 10 # Offset to the new row
### Create a range list for merged cells on new row
new_merge_list = get_merge_list(row_range, row_offset)
### Create merged cells on new row
for nm in new_merge_list:
ws.merge_cells(nm)
### Copy cell values to new row
copy_range(row_range, ws, row_offset)
### Save workbook
wb.save("foo_out.xlsx")
Related
My question is simple and I'm sorry to ask it here. But I tried several ways to iterate through my excel file and I'm having trouble finding the solution.
from openpyxl import workbook, load_workbook
wb = load_workbook("italian_team.xlsx")
ws = wb.active
rows = ws["A"]
equipe = ["Juventus", "Ac Milan", "Torino", "Pescara", "As Roma", "Genoa", "Napoli"]
for cell in rows:
x = equipe[cell]
wb.save("italian_team.xlsx")
Do you mean you just want to insert your list as a row in the workbook?
If so there are a few options, you could just append the list as is to the sheet in which case it will be enter after the last used row.
Or specify the row (and column) to add to.
Both options are shown in the code below
from openpyxl import workbook, load_workbook
wb = load_workbook("italian_team.xlsx")
ws = wb.active
# rows = ws["A"]
equipe = ["Juventus", "Ac Milan", "Torino", "Pescara", "As Roma", "Genoa", "Napoli"]
# for cell in rows:
# x = equipe[cell]
# This will append the list after the last used row
ws.append(equipe)
# This will enter the list at row 1 column 1 to the length of the list
# Use min_row = and max_col = as well if the list is to be on another row or start at another column
for row in ws.iter_rows(max_row=1, max_col=len(equipe)):
for enum, cell in enumerate(row):
cell.value = equipe[enum]
wb.save("italian_team.xlsx")
If the cell contains "external" from the C column then copy cell "good" from the D column, into the E column, in the rows where the A column contains 003.
Below are two images (before and after) in excel.
Before:
After:
I tried to find a correct script but it did not work out. It needs to be changed to "row" and "column" where I put "???" :
import openpyxl
from openpyxl import load_workbook
wb_source = openpyxl.load_workbook("path/file.xlsx")
sheet = wb_source['Sheet1']
x=sheet.max_row
y=sheet.max_column
for r in range(1, x+1) :
for j in range(1, y+1):
copy(sheet.cell(row= ???, column=???)
if str(copy.value)=="external":
sheet.??
break
wb_source.save("path/file2.xlsx")
How should they be added (row and column)?
Read the entire sheet.
Create a dictionary for the external products
Write back to Excel.
Try:
import openpyxl
wb = openpyxl.load_workbook("file1.xlsx")
ws = wb['Sheet1']
data = list()
for r, row in enumerate(ws.iter_rows()):
data.append([cell.value for c, cell in enumerate(row)])
mapper = {l[0]: l[-1] for l in data if l[2]=="external"}
for r, row in enumerate(ws.iter_rows()):
if ws.cell(r+1, 1).value in mapper:
ws.cell(r+1, 5).value = mapper[ws.cell(r+1, 1).value]
wb.save("file2.xlsx")
I am looking to import some data into a google sheet, and am using numpy to transpose the list of lists so it can be uploaded into gsheet.
When i run the script i get the following error
IndexError: index 0 is out of bounds for axis 0 with size 0
def update_sheet(ws, rows, left=1, top=2):
"""
updates the google spreadsheet with given table
- ws is gspread.models.Worksheet object
- rows is a table (list of lists)
- left is the number of the first column in the target document (beginning with 1)
- top is the number of first row in the target document (beginning with 1)
"""
# number of rows and columns
num_lines, num_columns = len(rows), len(rows[0])
# selection of the range that will be updated
cell_list = ws.range(
colrow_to_A1(left,top)+':'+colrow_to_A1(left+num_columns-1, top+num_lines-1)
)
# modifying the values in the range
for cell in cell_list:
val = rows[cell.row-top][cell.col-left]
cell.value = val
# update in batch
ws.update_cells(cell_list, value_input_option='USER_ENTERED')
# read csv into pandas to manipulate the layout and edit the column headings and remove euro symbols
df = pd.read_csv(file)
df = df.iloc[2:] # remove the first 2 rows from the dataframe
#rename the df columns as it reads them in really wierd
#df['Amount'] = df['Amount'].str[2:]
df.to_csv(file, index=False)
# read in the CSV as a list of lists
with open(file) as csvfile:
rows = csv.reader(csvfile)
res = list(map(list, zip(*rows)))
# print(*res)
# delete the first column of the transposed list of lists - this means we no longer have the headers (couldn't work out how to transpose with map, list, zip, etc.)
for row in res:
del row[0]
# use numpy to transpose the list of lists so it can be uploaded into gsheet
res2 = np.array(res).T.tolist()
res2 = np.delete(res2, (0), axis=0) # removes top row from array (title row from csv export)
res2 = res2[:-3, :] # removes last 3 rows from array (superfluous export details from export)
ws = worksheet
update_sheet(ws, res2)
Hi so I am trying to copy and paste W7:W46 column into another worksheet. The code I have so far,
col_j = New_Burden['W']
for idx, cell in enumerate(col_j,1):
ws1.cell(row = idx, column = 10).value = cell.value
is able to copy over the entire column, but unfortunately transfers the various headers as well. One solution I have tried is:
for row in New_Burden['W7:W46']:
for cell in row:
ws1.cell(row = 2, column = 10).value = cell.value
But that only copies the first value of W7
Copy a Range(['W7:W46']) from one Worksheet to another Worksheet:
If the Ranges are not overlapping, it's also possible in the same Worksheet.
from openpyxl import Workbook
# Create a new Workbook
wb = Workbook()
ws = wb.worksheets[0]
from openpyxl.utils import range_boundaries
# Define start Range(['J2']) in the new Worksheet
min_col, min_row, max_col, max_row = range_boundaries('J2')
# Iterate Range you want to copy
for row, row_cells in enumerate(New_Burden['W7:W46'], min_row):
for column, cell in enumerate(row_cells, min_col):
# Copy Value from Copy.Cell to given Worksheet.Cell
ws.cell(row=row, column=column).value = cell.value
If you want to do the above with multiple different Columns,
use the above in a function:
def copy_range(source_range, target_start):
# Define start Range(target_start) in the new Worksheet
min_col, min_row, max_col, max_row = range_boundaries(target_start)
# Iterate Range you want to copy
for row, row_cells in enumerate(New_Burden[source_range], min_row):
for column, cell in enumerate(row_cells, min_col):
# Copy Value from Copy.Cell to given Worksheet.Cell
ws.cell(row=row, column=column).value = cell.value
for source_range, target_start in [('W7:W46','J2'), ('Y7:Y46','A2')]:
copy_range(source_range, target_start)
Tested with Python: 3.4.2 - openpyxl: 2.4.1 - LibreOffice: 4.3.3.2
I have a worksheet that is updated every week with thousands of rows and would need to transfer rows from this worksheet after filtering. I am using the current code to find the cells which has the value I need and then transfer the entire row to another sheet but after saving the file, I get the "IndexError: list index out of range" exception.
The code I use is as follows:
import openpyxl
wb1 = openpyxl.load_workbook('file1.xlsx')
wb2 = openpyxl.load_workbook('file2.xlsx')
ws1 = wb1.active
ws2 = wb2.active
for row in ws1.iter_rows():
for cell in row:
if cell.value == 'TrueValue':
n = 'A' + str(cell.row) + ':' + ('GH' + str(cell.row))
for row2 in ws1.iter_rows(n):
ws2.append(row2)
wb2.save("file2.xlsx")
The original code I used that used to work is below and has to be modified because of the large files which causes MS Excel not to open them (over 40mb).
n = 'A3' + ':' + ('GH'+ str(ws1.max_row))
for row in ws1.iter_rows(n):
ws2.append(row)
Thanks.
I'm not entirely sure what you're trying to do but I suspect the problem is that you have nested your copy loop.
Try the following:
row_nr = 1
for row in ws1:
for cell in row:
if cell.value == "TrueValue":
row_nr = cell.row
break
if row_nr > 1:
break
for row in ws1.iter_rows(min_row=row_nr, max_col=190):
ws2.append((cell.value for cell in row))
Question: I get the "IndexError: list index out of range" exception.
I get, from ws1.iter_rows(n)
UserWarning: Using a range string is deprecated. Use ws[range_string]
and from ws2.append(row2).
ValueError: Cells cannot be copied from other worksheets
The Reason are row2 does hold a list of Cell objects instead of a list of Values
Question: ... need to transfer rows from this worksheet after filtering
The following do what you want, for instance:
# If you want to Start at Row 2 to append Row Data
# Set Private self._current_row to 1
ws2.cell(row=1, column=1).value = ws2.cell(row=1, column=1).value
# Define min/max Column Range to copy
from openpyxl.utils import range_boundaries
min_col, min_row, max_col, max_row = range_boundaries('A:GH')
# Define Cell Index (0 Based) used to Check Value
check = 0 # == A
for row in ws1.iter_rows():
if row[check].value == 'TrueValue':
# Copy Row Values
# We deal with Tuple Index 0 Based, so min_col must have to be -1
ws2.append((cell.value for cell in row[min_col-1:max_col]))
Tested with Python: 3.4.2 - openpyxl: 2.4.1 - LibreOffice: 4.3.3.2
Use a list to hold the items in each column for the particular row.
Then append the list to your ws2.
...
def iter_rows(ws,n): #produce the list of items in the particular row
for row in ws.iter_rows(n):
yield [cell.value for cell in row]
for row in ws1.iter_rows():
for cell in row:
if cell.value == 'TrueValue':
n = 'A' + str(cell.row) + ':' + ('GH' + str(cell.row))
list_to_append = list(iter_rows(ws1,n))
for items in list_to_append:
ws2.append(items)
I was able to solve this with lists for my project.
import openpyxl
#load data file
wb1 = openpyxl.load_workbook('original.xlsx')
sheet1 = wb1.active
print("loaded 1st file")
#new template file
wb2 = openpyxl.load_workbook('blank.xlsx')
sheet2 = wb2.active
print("loaded 2nd file")
header = sheet1[1:1] #grab header row
listH =[]
for h in header:
listH.append(h.value)
sheet2.append(listH)
colOfInterest= 11 # this is my col that contains the value I'm checking against
for rowNum in range(2, sheet1.max_row +1): #iterate over each row, starting with 2 to skipping header from original file
if sheet1.cell(row=rowNum, column=colOfInterest).value is not None: #interested in non blank values in column 11
listA = [] # list which will hold my data
row = sheet1[rowNum:rowNum] #creates a tuple of row's data
#print (str(rowNum)) # for debugging to show what rows are copied
for cell in row: # for each cell in the row
listA.append(cell.value) # add each cell's data as an element in the list
if listA[10] == 1: # condition1 I'm checking for by looking up the index in the list
sheet2.append(listA) # appending the sheet2's next available row
elif listA[10] > 1: # condition2 I'm checking for by looking up the index in the list
# do something else and store it in bar
sheet2.append(bar) # appending the sheet2's next available row
print("saving file...")
wb2.save('result.xlsx') # save file
print("Done!")
Tested with: Python 3.7 openpyxl 2.5.4