0

I have a list called "Names.

Names = [['Bassett', 'Richard', '1745-04-02', 'M', 'sen', 'DE', 'Anti-Administration', 1745], ['Bland', 'Theodorick', '1742-03-21', 'M', 'rep', 'VA', '', 1742], ['Burke', 'Aedanus', '1743-06-16', 'M', 'rep', 'SC', '', 0]]

In the names list if the year column (index 7) equals 0 then I want to replace it with the previous year value. For eg: the third list's year value is 0, I want to replace it with 1742.

MJP
  • 1,547
  • 2
  • 14
  • 21

4 Answers4

1

You need to store the latest valid year and pass it on to the next stage. reduce passes a value from one stage to the next and since lists are passed by reference we can modify the lists in place.

Names = [['Bassett', 'Richard', '1745-04-02', 'M', 'sen', 'DE', 'Anti-Administration', 1745],
         ['Bland', 'Theodorick', '1742-03-21', 'M', 'rep', 'VA', '', 1742],
         ['Burke', 'Aedanus', '1743-06-16', 'M', 'rep', 'SC', '', 0]]

def fill_year(year, ns):
    if ns[7] == 0:
        ns[7] = year
    return ns[7]


reduce(fill_year, Names, 0)
print Names

Apparently reduce is deprecated in python3.

Try:

year = 0
for ns in Names:
    ns[7] = year if ns[7] == 0 else ns[7]
    year = ns[7]
Mike Robins
  • 1,733
  • 10
  • 14
  • @MJP looking at the [docs](https://stackoverflow.com/questions/13638898/how-to-use-filter-map-and-reduce-in-python-3), no. _Removed reduce(). Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable._ – Mike Robins May 22 '18 at 01:59
  • Hi Mike, the second code is much more readable. I will go with it. – MJP May 22 '18 at 17:23
  • The second code is actually more what I was aiming for when I took on the question. It is probably even more readable to code it like the `fill_year` with the explicit `if` statement rather than the conditional expression. – Mike Robins May 23 '18 at 00:32
  • Yes, I agree. But, the reduce function doesn't work in Python 3. That's the reason why I didn't use it. – MJP May 23 '18 at 01:17
  • Sorry if I was not clear, I meant rather than `ns[7] = year if ns[7] == 0 else ns[7]` code the assignment as `if ns[7] == 0: ns[7] = year`. – Mike Robins May 23 '18 at 05:32
0

You can try this :

Names = [['Bassett', 'Richard', '1745-04-02', 'M', 'sen', 'DE', 'Anti-Administration', 1745],
     ['Bland', 'Theodorick', '1742-03-21', 'M', 'rep', 'VA', '', 1742],
     ['Burke', 'Aedanus', '1743-06-16', 'M', 'rep', 'SC', '', 0]]

for ele in range(len(Names)):
    if Names[ele][7] == 0:
        Names[ele][7] = (Names[ele-1][2].split('-'))[0]

print(Names)

Explanation:

Using for-loop and range(len()) itearte over the length number of times

for ele in range(len(Names)): #it will iterate over three times as len -> 3

Next check for the year value at index 7 if it equals to 0 then copy the previous year means the year present in previous iteration step means ele-1. For example if it is in ele(2nd iteration) then it will fetch the year from ele-1 (1st iteration).

  Names[ele][7] = (Names[ele-1][2].split('-'))[0]

The year is combined in date format. To retrieve only the year use the split() to split the string using - as delimiter

 '1742-03-21' -> [1742, 03, 21]

So the year is in index 0.

 (Names[ele-1][2].split('-'))[0] -> we get year from here

Finally update the year of current ele to the one which we got.

Output:

[['Bassett', 'Richard', '1745-04-02', 'M', 'sen', 'DE', 'Anti-Administration', 1745], ['Bland', 'Theodorick', '1742-03-21', 'M', 'rep', 'VA', '', 1742], ['Burke', 'Aedanus', '1743-06-16', 'M', 'rep', 'SC', '', '1742']]
  • @MJP: Is this what you are expecting ? –  May 22 '18 at 01:35
  • The code works perfectly. But I am unable to understand this part: Names[ele][7] = (Names[ele-1][2].split('-'))[0] – MJP May 22 '18 at 01:48
  • @MJP: Let me know if you got clarified. or need more information ? –  May 22 '18 at 02:02
  • I think you don't have to split the date here. You just need to use the "year" value in index 7. I understood the code!! – MJP May 22 '18 at 16:50
  • @MJP: But if the consecutive elements have `0` at index 7 like this `[['Bassett', 'Richard', '1745-04-02', 'M', 'sen', 'DE', 'Anti-Administration', 1745], ['Bland', 'Theodorick', '1742-03-21', 'M', 'rep', 'VA', '', 0], ['Burke', 'Aedanus', '1743-06-16', 'M', 'rep', 'SC', '', 0]]` ? That's why I used split to get the year from previous element –  May 23 '18 at 01:18
  • @MJP: If you think you got desired answer please confirm the answer by voting –  May 23 '18 at 01:23
0

The Pandas package has a function for this and is useful for many other types of operations on tabular data.

If you are willing to use it you could solve this fill problem as follows:

import pandas as pd
df = pd.DataFrame(Names, columns=['A', 'B', 'C', 'D', 'E', 'F', 'G', 'Year'])
df['Year'] = df['Year'].replace({0: None}).fillna(method='ffill')
print(df)

Output:

         A           B           C  D    E   F                    G  Year
0  Bassett     Richard  1745-04-02  M  sen  DE  Anti-Administration  1745
1    Bland  Theodorick  1742-03-21  M  rep  VA                       1742
2    Burke     Aedanus  1743-06-16  M  rep  SC                       1742

UPDATE:

As pointed out by @miradulo, Series.replace has a method argument so you can do the operation in one go as follows:

df['Year'] = df['Year'].replace(0, method='ffill')
Bill
  • 10,323
  • 10
  • 62
  • 85
-1

You can use unpacking:

Names = [['Bassett', 'Richard', '1745-04-02', 'M', 'sen', 'DE', 'Anti-Administration', 1745], ['Bland', 'Theodorick', '1742-03-21', 'M', 'rep', 'VA', '', 1742], ['Burke', 'Aedanus', '1743-06-16', 'M', 'rep', 'SC', '', 0]]
new_names = [b+[Names[i-1][-1]] if not a else [*b, a] for i, [*b, a] in enumerate(Names)]

Output:

[['Bassett', 'Richard', '1745-04-02', 'M', 'sen', 'DE', 'Anti-Administration', 1745], ['Bland', 'Theodorick', '1742-03-21', 'M', 'rep', 'VA', '', 1742], ['Burke', 'Aedanus', '1743-06-16', 'M', 'rep', 'SC', '', 1742]]
Ajax1234
  • 69,937
  • 8
  • 61
  • 102