MapAt[f,#,List/@Cases[PositionIndex[#],x_/;Length[x]>1:>Sequence@@x]]&@list
(* {f[f1],f[f2],f[f1],f3,f[f2],f[f1],f4} *)
mapAtDuplicates
mapAtDuplicates:=Function[{list,fn},ReplacePart[list,First@#1->fn[#2,
Last@#1]&@@@Thread[{#2,#1}]&@@@(MapAt[MapIndexed[{#1,First@#2}&],
{2}]/@(KeyValueMap[List]@(Select[PositionIndex[list], Length[#]>1&])))//Flatten]]
mapAtDuplicates[list,{#1,#2}&]
(* {{f1,1},{f2,1},{f1,2},f3,{f2,2},{f1,3},f4} *)
mapAtDuplicates[list,#1[#2]&]
(* {f1[1],f2[1],f1[2],f3,f2[2],f1[3],f4} *)
mapAtDuplicates[list,#1<>"_"<>ToString[#2]&]
(* {f1_1,f2_1,f1_2,f3,f2_2,f1_3,f4} *)
Just for Fun: Map Duplicates in a Protein Sequence
(i) Import a sequence (bovine serum albumin, 607 amino acids, single letter code)
importedSequence = Import["http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=protein&id=3336842&rettype=fasta&retmode=text", "Data"];
{bsaSequenceHeading, bsaSequenceData} = {#[[1,1]], #[[2,1]]} &@importedSequence;
(ii) Map Duplicates
Short[mapAtDuplicates[StringSplit[bsaSequenceData,""],#1[#2]&],10]
(*
{M[1],K[1],W[1],V[1],T[1],F[1],I[1],S[1],L[1],L[2],
L[3],L[4],F[2],S[2],S[3],A[1],Y[1],S[4],R[1],G[1],
V[2],F[3],R[2],R[3],D[1],T[2],H[1],K[2],S[5],E[1],
I[2],A[2],H[2],R[4],F[4],K[3],D[2],L[5],<<531>>,V[33],M[5],
E[57],N[14],F[28],V[34],A[41],F[29],V[35],D[38],K[58],C[33],
C[34],A[42],A[43],D[39],D[40],K[59],E[58],A[44],C[35],F[30],
A[45],V[36],E[59],G[17],P[28],K[60],L[64],V[37],V[38],S[32],
T[34],Q[20],T[35],A[46],L[65],A[47] } *)
But a criticism of this approach might be that only duplicates are indexed.
(iii) Import a sequence with singletons (Insulin B chain)
importedSequenceTwo = Import["http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=protein&id=2WS1_B&rettype=fasta&retmode=text", "Data"];
{insulinBChainHeading, insulinBChainData} = {#[[1,1]], #[[2,1]]} &@importedSequenceTwo;
(iv) Map Duplicates
mapAtDuplicates[StringSplit[insulinBChainData,""], #1[#2]&]
(*
{F[1],V[1],N,Q,H[1],L[1],C[1],G[1],S,H[2],
L[2],V[2],E[1],A,L[3],Y,L[4],V[3],C[2],G[2],
E[2],R,G[3],F[2],F[3],X,T[1],P,K,T[2]}
*)
cumIndex
cumIndex:=Function[list,ReplacePart[list,First@#1->#2[Last@#1]&@@@Thread[
{#2,#1}]&@@@(MapAt[MapIndexed[{#1,First@#2}&],{2}]/@(KeyValueMap[List]@(
PositionIndex[list])))//Flatten]]
(v) Cumulative Index
cumIndex[StringSplit[insulinBChainData,""]]
(*
{F[1],V[1],N[1],Q[1],H[1],L[1],C[1],G[1],S[1],H[2],
L[2],V[2],E[1],A[1],L[3],Y[1],L[4],V[3],C[2],G[2],
E[2],R[1],G[3],F[2],F[3],X[1],T[1],P[1],K[1],T[2]}
*)